﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using System.Dynamic;
using System.IO;

using Roslyn.Compilers.CSharp;
using Roslyn.Compilers;
using Roslyn.Compilers.Common;
using AttributeData = Roslyn.Compilers.CSharp.AttributeData;

using King.Loader;
using King.Extensions;
using King.Reflection;
//using King.CSharp.Text;
using King.Text;
using System.Globalization;
using System.Text.RegularExpressions;

namespace King.CSharp.Reflection {

    public class CSharpAssemblyName {
        public static CSharpAssemblyName GetAssemblyName(string path) {
            return AssemblyName.GetAssemblyName(path);
        }
        public static implicit operator CSharpAssemblyName(AssemblyName name) {
            return new CSharpAssemblyName(name);
        }
        public static implicit operator AssemblyName(CSharpAssemblyName name) {
            return new AssemblyName(name.ToString());
        }

        private AssemblyName m_assemblyName;

        public CSharpAssemblyName(AssemblyName assemblyName) {
            m_assemblyName = assemblyName;
        }
        public CSharpAssemblyName(string name) {
            m_assemblyName = new AssemblyName(name);
        }

        public bool IsStrongNamed {
            get { return m_assemblyName.GetPublicKeyToken().Any(); }
        }
        public string Name {
            get { return m_assemblyName.Name; }
        }
        public string Version {
            get {
                if (!IsStrongNamed)
                    return null;

                if (m_assemblyName.Version == null)
                    return null;

                return m_assemblyName.Version.ToString(); 
            }
        }
        public string PublicKeyToken {
            get {
                if (!IsStrongNamed) 
                    return null;

                var result = m_assemblyName.GetPublicKeyToken()
                    .StringJoin(string.Empty, o => o.ToString("x2"));

                return result;
            }
        }

        public override string ToString() {
            var result = new TextJoin(',') {
                Name,
                Version == null ? null : "Version=" + Version,
                "Culture=neutral",
                PublicKeyToken == null ? null : "PublicKeyToken=" + PublicKeyToken
            }.ReadToEnd();

            return result;
        }
        public override int GetHashCode() {
            var hashcodeString = Name.ToLower() + Version + PublicKeyToken;
            return hashcodeString.GetHashCode();
        }
        public override bool Equals(object obj) {
            if (obj is string)
                obj = new CSharpAssemblyName((string)obj);

            var rhs = obj as CSharpAssemblyName;
            if (rhs == null)
                return false;

            if (Name.ToLower() != rhs.Name.ToLower())
                return false;

            if (Version != rhs.Version)
                return false;

            if (PublicKeyToken != rhs.PublicKeyToken)
                return false;

            return true;
        }
    }

    public enum CSharpTargetType {
        Console = OutputKind.ConsoleApplication,
        Windows = OutputKind.WindowsApplication,
        Library = OutputKind.DynamicallyLinkedLibrary,
    }
    public enum CSharpPlatformType {
        AnyCpu = Platform.AnyCPU,
        X86 = Platform.x86,
        X64 = Platform.x64,
    }

    public sealed class CSharpCompilation {
#if false
        private static IEnumerable<MetadataReference> MergeReferences(
            IEnumerable<string> fileReferences,
            IEnumerable<string> assemblyReferences) {

            if (fileReferences != null) {
                foreach (var path in fileReferences)
                    yield return new MetadataFileReference(path);
            }

            if (assemblyReferences != null) {
                foreach (var name in assemblyReferences)
                    yield return MetadataReference.CreateAssemblyReference(name);
            }
        }

        private readonly Compilation m_compilation;
        private readonly Loader<CSharpAssembly> m_assemblies;
        private readonly Loader<CSharpMember> m_members;
        private readonly Lazy<Dictionary<string, string>> m_keywordNameByFullname;
        private readonly Lazy<CSharpAssembly> m_assembly;

        internal CSharpCompilation(Compilation compilation) {

            m_compilation = compilation;
            m_assembly = new Lazy<CSharpAssembly>(() =>
                new CSharpAssembly(this, compilation.Assembly));



        }
        public CSharpCompilation(
            string name = "program",
            IEnumerable<string> libraryReferences = null,
            IEnumerable<string> assemblyReferences = null,
            IEnumerable<string> sourceFiles = null,
            CSharpTargetType target = CSharpTargetType.Library,
            CSharpPlatformType platform = CSharpPlatformType.AnyCpu,
            bool allowUnsafe = false,
            bool debugInformation = false,
            IEnumerable<string> preprocessorSymbols = null
        ) : this(
            Compilation.Create(
                outputName: name,
                //fileResolver: null,
                options: new CompilationOptions(
                    outputKind: (OutputKind)target,
                    platform: (Platform)platform,
                    allowUnsafe: allowUnsafe,
                    debugInformationKind: debugInformation ?
                        DebugInformationKind.PDBOnly :
                        DebugInformationKind.None
                )
            ).AddReferences(
                MergeReferences(
                    libraryReferences,
                    assemblyReferences
                )
            ).AddSyntaxTrees(sourceFiles == null ? 
                Enumerable.Empty<SyntaxTree>() :
                sourceFiles.Select(o => 
                    SyntaxTree.ParseText(o, 
                        options: new ParseOptions(
                            parseDocumentationComments: true,
                            preprocessorSymbols: preprocessorSymbols == null ? 
                                ReadOnlyArray<string>.Empty :
                                ReadOnlyArray<string>.CreateFrom(preprocessorSymbols)
                        )
                    )
                )
            )
        ) {
        }

        internal SemanticModel GetSemanticModel(SyntaxTree tree) {
            return m_compilation.GetSemanticModel(tree);
        }

        internal CSharpAssembly GetAssembly(AssemblySymbol assembly) {
            return m_assemblies.Get(assembly);
        }
        internal CSharpAssembly TryGetAssembly(AssemblySymbol assembly) {
            return m_assemblies.TryGet(assembly);
        }

        internal CSharpMember GetMember(Symbol type) {
            return m_members.Get(type);
        }
        internal CSharpMember TryGetMember(Symbol type) {
            return m_members.TryGet(type);
        }

        internal CSharpType GetType(TypeSymbol type) {
            return (CSharpType)GetMember(type);
        }
        internal CSharpType TryGetType(TypeSymbol type) {
            return (CSharpType)TryGetMember(type);
        }

        internal TypeSymbol TryGetType(TypeName assemblyQualifiedName) {
            if (assemblyQualifiedName == null)
                return null;

            // resolve assembly
            var assemblyName = new AssemblyName(assemblyQualifiedName.AssemblyFullName);
            var assembly = TryGetAssembly(assemblyName);
            if (assembly == null)
                return null;

            // resolve type
            return assembly.TryGetType(assemblyQualifiedName.MakeAssembly(null));
        }
        internal TypeSymbol MakePointerType(TypeSymbol typeSymbol) {
            return m_compilation.CreatePointerTypeSymbol(typeSymbol);
        }
        internal TypeSymbol MakeArrayType(TypeSymbol typeSymbol, int rank = 1) {
            return m_compilation.CreateArrayTypeSymbol(typeSymbol, rank);
        }
        internal string TryGetTypeKeyword(CSharpType type) {
            if (type == null)
                return null;

            if (!type.Assembly.IsMscorlib)
                return null;

            return m_keywordNameByFullname.Value.GetValueOrDefault(type.MetadataFullName);
        }

        public CSharpAssembly TryGetAssembly(AssemblyName name) {
            return m_assemblies.TryGet(name);
        }
        public CSharpAssembly GetAssembly(AssemblyName name) {
            return m_assemblies.Get(name);
        }

        public CSharpAssembly TryGetAssembly(string path) {
            return m_assemblies.TryGet(path);
        }
        public CSharpAssembly GetAssembly(string path) {
            return m_assemblies.Get(path);
        }

        public IEnumerable<CSharpAssembly> Assemblies() {
            return m_assemblies;
        }

        public CSharpAssembly TryGetAssembly(Assembly assembly) {
            if (assembly == null)
                return null;
            return GetAssembly(assembly.FullName);
        }
        public CSharpAssembly GetAssembly(Assembly assembly) {
            if (assembly == null)
                throw new ArgumentNullException();
            return GetAssembly(assembly.FullName);
        }

        public CSharpType TryGetType(string name) {
            if (name == null)
                return null;

            var typeName = TypeName.Parse(name);
            if (typeName.AssemblyFullName == null)
                return null;

            var assembly = TryGetAssembly(new AssemblyName(typeName.AssemblyFullName));
            if (assembly == null)
                return null;

            return assembly.TryGetType(typeName.FullName);
        }
        public CSharpType GetType(string name) {
            if (name == null)
                throw new ArgumentNullException();

            var type = TryGetType(name);
            if (type == null)
                throw new ArgumentException();

            return type;
        }

        public CSharpType TryGetType(Type type) {
            if (type == null)
                return null;
            return GetType(type.AssemblyQualifiedName);
        }
        public CSharpType GetType(Type type) {
            if (type == null)
                return null;
            return GetType(type.AssemblyQualifiedName);
        }

        public CSharpAssembly Assembly {
            get { return m_assembly.Value; }
        }
#endif
    }

    public sealed class CSharpLoader {

        public static CSharpLoader Create() {
            return s_runtimeLoader.Value;
        }
        public static CSharpLoader Create(
            IEnumerable<string> dirs) {

            return Create(o => {

                foreach (var dir in dirs) {

                    var pathWithOutExtension = Path.Combine(dir, o.Name);
                    var exePath = pathWithOutExtension + ".exe";
                    var dllPath = pathWithOutExtension + ".dll";

                    var path = exePath;
                    if (!File.Exists(path))
                        path = dllPath;

                    if (!File.Exists(path))
                        continue;

                    return Assembly.ReflectionOnlyLoadFrom(path);
                }

                return Assembly.ReflectionOnlyLoad(o.ToString());
            });
        }
        public static CSharpLoader Create(
            Func<CSharpAssemblyName, Assembly> resolveAssembly) {

            // assert this is only use of reflection only context
            if (AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies().Any())
                throw new InvalidOperationException(
                    "Reflection only context already in use by this AppDomain.");

            // linkup assembly resolve event
            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve +=
                (o, a) => resolveAssembly(new CSharpAssemblyName(a.Name));

            return new CSharpLoader(
                assemblyNames: o => AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies(),
                assemblyFactory: (loader, o) =>
                    new CSharpAssembly(
                        loader: loader,

                        // user resolves assembly referneces (readonly)
                        assemblySymbol: new ReflectionAssembly(resolveAssembly(o))
                    ),
                symbolFactory: (loader, name) => ReflectionSymbolFactory(loader, name)
            );
        }
        public static CSharpLoader Create(
            string assemblyName = "program",
            IEnumerable<string> libraryReferences = null,
            IEnumerable<string> assemblyReferences = null,
            IEnumerable<string> sourceFiles = null,
            IEnumerable<string> preprocessorSymbols = null) {

            // associate library/assembly references with an AssemblyName
            var references = (
                from o in libraryReferences
                select new {
                    // library reference = path to a file
                    AssemblyName = CSharpAssemblyName.GetAssemblyName(o),
                    MetadataReference = (MetadataReference)new MetadataFileReference(o)
                }
            ).Union(
                from o in assemblyReferences
                select new {
                    // assembly reference = assembly qualified name
                    AssemblyName = new CSharpAssemblyName(o),
                    MetadataReference = MetadataReference.CreateAssemblyReference(o)
                }
            ).ToDictionary(
                o => o.AssemblyName, 
                o => o.MetadataReference
            );

            // create a syntax tree for every source file
            var syntaxTrees = Enumerable.Empty<SyntaxTree>();
            if (sourceFiles != null) {
                syntaxTrees = sourceFiles.Select(o =>
                    SyntaxTree.ParseText(o,
                        options: new ParseOptions(
                            parseDocumentationComments: true,
                            preprocessorSymbols: preprocessorSymbols == null ?
                                ReadOnlyArray<string>.Empty :
                                ReadOnlyArray<string>.CreateFrom(preprocessorSymbols)
                        )
                    )
                );
            }

            // compilation references for all provided assemblies
            var compilation = Compilation
                .Create(outputName: assemblyName)
                .AddReferences(references.Values)
                .AddSyntaxTrees(syntaxTrees);

            return new CSharpLoader(
                assemblyNames: o => references.Keys,
                assemblyFactory: (loader, name) => {

                    // look up reference given name
                    var reference = references.GetValueOrDefault(name);
                    if (reference == null)
                        return null;

                    // construct assembly
                    return new CSharpAssembly(
                        loader: loader,
                        assemblySymbol: new RoslynAssembly(
                            compilation.GetReferencedAssemblySymbol(
                                compilation.ExternalReferences.Single()
                            )
                        )
                    );
                },
                symbolFactory: (loader, name) => {

                    var typeSymbol = name as TypeSymbol;
                    if (typeSymbol != null)
                        return new CSharpType(loader, new RoslynType(typeSymbol));

                    var methodSymbol = name as MethodSymbol;
                    if (typeSymbol != null)
                        return new CSharpMethod(loader, new RoslynMethod(methodSymbol));

                    var propertySymbol = name as PropertySymbol;
                    if (typeSymbol != null)
                        return new CSharpProperty(loader, new RoslynProperty(propertySymbol));

                    var fieldSymbol = name as FieldSymbol;
                    if (fieldSymbol != null) {
                        if (fieldSymbol.ContainingType.TypeKind == TypeKind.Enum)
                            return new CSharpEnumField(loader, new RoslynField(fieldSymbol));

                        return new CSharpField(loader, new RoslynField(fieldSymbol));
                    }

                    var eventSymbol = name as EventSymbol;
                    if (typeSymbol != null)
                        return new CSharpEvent(loader, new RoslynEvent(eventSymbol));

                    var parameterSymbol = name as ParameterSymbol;
                    if (typeSymbol != null)
                        return new CSharpParameter(loader, new RoslynParameter(parameterSymbol));

                    throw new NotSupportedException();
                }
            );
        }

        private static CSharpSymbol ReflectionSymbolFactory(CSharpLoader loader, object name) {
            var typeSymbol = name as Type;
            if (typeSymbol != null)
                return new CSharpType(loader, new ReflectionType(typeSymbol));

            var methodSymbol = name as MethodBase;
            if (methodSymbol != null)
                return new CSharpMethod(loader, new ReflectionMethod(methodSymbol));

            var propertySymbol = name as PropertyInfo;
            if (propertySymbol != null)
                return new CSharpProperty(loader, new ReflectionProperty(propertySymbol));

            var fieldSymbol = name as FieldInfo;
            if (fieldSymbol != null) {
                if (fieldSymbol.DeclaringType.IsEnum)
                    return new CSharpEnumField(loader, new ReflectionField(fieldSymbol));

                return new CSharpField(loader, new ReflectionField(fieldSymbol));
            }

            var eventSymbol = name as EventInfo;
            if (eventSymbol != null)
                return new CSharpEvent(loader, new ReflectionEvent(eventSymbol));

            var parameterSymbol = name as ParameterInfo;
            if (parameterSymbol != null)
                return new CSharpParameter(loader, new ReflectionParameter(parameterSymbol));

            throw new NotSupportedException();
        }

        static CSharpLoader() {
            s_runtimeLoader = new Lazy<CSharpLoader>(() =>
                new CSharpLoader(
                    assemblyNames: o => Enumerable.Empty<object>(),
                    assemblyFactory: (loader, o) => {
                        try {
                            var assembly = Assembly.Load(o);

                            return new CSharpAssembly(
                                loader: loader,

                                // use fusion to resolve assembly references
                                assemblySymbol: new ReflectionAssembly(assembly)
                            );

                        } catch { return null; }
                    },
                    symbolFactory: (loader, name) => ReflectionSymbolFactory(loader, name)
                )
            );
        }

        private static readonly Lazy<CSharpLoader> s_runtimeLoader;

        private readonly Loader<CSharpAssemblyName, CSharpAssembly> m_assemblies;
        private readonly Loader<object, CSharpSymbol> m_symbols;
        private readonly Lazy<Dictionary<string, string>> m_keywordNameByFullname;

        internal CSharpLoader(
            Func<CSharpLoader, IEnumerable<object>> assemblyNames,
            Func<CSharpLoader, CSharpAssemblyName, CSharpAssembly> assemblyFactory,
            Func<CSharpLoader, object, CSharpSymbol> symbolFactory) {

            m_assemblies = new Loader<CSharpAssemblyName, CSharpAssembly>(
                keys: o => assemblyNames(this),
                defaultResolver: o => assemblyFactory(this, o.Key),
                getKeys: o => new object[] {
                    o.Object,
                    o.AssemblyName,
                },
                normalizeKey: o => {
                    var assemblyName = o as CSharpAssemblyName;
                    if (assemblyName == null) {

                        var displayName = o as string;
                        if (displayName == null) {

                            var assembly = o as Assembly;
                            if (assembly == null)
                                return null;

                            displayName = assembly.FullName;
                        }

                        assemblyName = new CSharpAssemblyName(displayName);
                    }

                    return assemblyName;
                },
                getLoader: o => o.Loader.m_assemblies
            );

            m_symbols = new Loader<object, CSharpSymbol>(
                keys: o => Enumerable.Empty<CSharpMember>(),
                getKeys: o => new object[] { 
                    o.Object 
                },
                getLoader: o => o.Loader.m_symbols,
                normalizeKey: o => o,
                defaultResolver: o => symbolFactory(this, o.Key)
            );

            m_keywordNameByFullname = new Lazy<Dictionary<string, string>>(() =>
                new[] {
                    new { Type = typeof(object), Keyword = "object" },
                    new { Type = typeof(void), Keyword = "void" },

                    new { Type = typeof(char), Keyword = "char" },
                    new { Type = typeof(string), Keyword = "string" },

                    new { Type = typeof(sbyte), Keyword = "sbyte" },
                    new { Type = typeof(short), Keyword = "short" },
                    new { Type = typeof(int), Keyword = "int" },
                    new { Type = typeof(long), Keyword = "long" },

                    new { Type = typeof(byte), Keyword = "byte" },
                    new { Type = typeof(ushort), Keyword = "ushort" },
                    new { Type = typeof(uint), Keyword = "uint" },
                    new { Type = typeof(ulong), Keyword = "ulong" },

                    new { Type = typeof(float), Keyword = "float" },
                    new { Type = typeof(double), Keyword = "double" },
                    new { Type = typeof(decimal), Keyword = "decimal" },
                }.ToDictionary(o => o.Type.FullName, o => o.Keyword));
        }

        internal CSharpType GetType(object typeSymbol) {
            return (CSharpType)GetSymbol(typeSymbol);
        }
        internal CSharpType TryGetType(object typeSymbol) {
            return (CSharpType)TryGetSymbol(typeSymbol);
        }

        internal CSharpSymbol GetSymbol(object type) {
            return m_symbols.Get(type);
        }
        internal CSharpSymbol TryGetSymbol(object type) {
            return m_symbols.TryGet(type);
        }

        internal CSharpAssembly GetAssembly(object assemblySymbol) {
            return m_assemblies.Get(assemblySymbol);
        }

        internal CSharpType MakePointerType(object typeSymbol) {
            throw new NotImplementedException();
        }
        internal CSharpType MakeArrayType(object typeSymbol, int rank = 1) {
            throw new NotImplementedException();
        }

        internal CSharpType TryGetType(TypeName typeName) {
            if (typeName.AssemblyFullName == null)
                return null;

            var assembly = TryGetAssembly(new CSharpAssemblyName(typeName.AssemblyFullName));
            if (assembly == null)
                return null;

            return assembly.TryGetType(typeName.FullName);
        }
        internal string TryGetTypeKeyword(CSharpType type) {
            if (type == null)
                return null;

            if (!type.Assembly.IsMscorlib)
                return null;

            return m_keywordNameByFullname.Value.GetValueOrDefault(type.MetadataFullName);
        }

        public CSharpAssembly TryGetAssembly(string name) {
            return m_assemblies.TryGet(name);
        }
        public CSharpAssembly TryGetAssembly(CSharpAssemblyName strongName) {
            return m_assemblies.TryGet(strongName);
        }
        public CSharpAssembly TryGetAssembly(Assembly assembly) {
            if (assembly == null)
                return null;
            return TryGetAssembly(new CSharpAssemblyName(assembly.FullName));
        }
        public CSharpType TryGetType(string name) {
            if (name == null)
                return null;

            var typeName = TypeName.Parse(name);
            return TryGetType(typeName);
        }
        public CSharpType TryGetType(Type type) {
            if (type == null)
                return null;
            return GetType(type.AssemblyQualifiedName);
        }

        public CSharpAssembly GetAssembly(string name) {
            return m_assemblies.Get(name);
        }
        public CSharpAssembly GetAssembly(CSharpAssemblyName name) {
            return m_assemblies.Get(name);
        }
        public CSharpAssembly GetAssembly(Assembly assembly) {
            if (assembly == null)
                throw new ArgumentNullException();
            return GetAssembly(new CSharpAssemblyName(assembly.FullName));
        }
        public CSharpType GetType(string name) {
            if (name == null)
                throw new ArgumentNullException();

            var type = TryGetType(name);
            if (type == null)
                throw new ArgumentException();

            return type;
        }
        public CSharpType GetType(Type type) {
            if (type == null)
                return null;
            return GetType(type.AssemblyQualifiedName);
        }
    }

    internal interface ICSharpSymbol {
        string Name { get; }
        object Object { get; }

        IEnumerable<ICSharpAttribute> AttributeData(CSharpType type, bool declaredOnly);
    }
    internal interface ICSharpAssembly : ICSharpSymbol {
        string Location { get; }
        CSharpAssemblyName AssemblyName { get; }

        IEnumerable<object> Types();
        object GetType(string name, string ns, int arity);

        Assembly Load();
    }
    internal interface ICSharpMember : ICSharpSymbol {
        object DeclaringMember { get; }
        object DeclaringAssembly { get; }

        bool IsProtected { get; }
        bool IsInternal { get; }
        bool IsPrivate { get; }
        bool IsPublic { get; }
    }
    internal interface ICSharpType : ICSharpMember {
        object ElementType { get; }
        int ArrayRank { get; }
        string MetadataName { get; }
        string Namespace { get; }

        object BaseType { get; }
        IEnumerable<object> Interfaces();

        bool IsAbstract { get; }
        bool IsSealed { get; }
        bool IsStatic { get; }

        bool IsPointer { get; }
        bool IsDelegate { get; }
        bool IsArray { get; }
        bool IsEnum { get; }
        bool IsStruct { get; }
        bool IsClass { get; }
        bool IsInterface { get; }
        bool IsReferenceType { get; }
        bool IsValueType { get; }

        object MakePointerType();
        object MakeArrayType(int? rank = null);
        object MakeReferenceType();

        bool IsSubclassOf(ICSharpType type);

        int GenericArity { get; }
        object GenericTypeDefinition { get; }
        IEnumerable<object> GenericArguments();
        object MakeGenericType(IEnumerable<object> enumerable);
        IEnumerable<object> GenericParameterConstraints();
        bool IsGenericParameter { get; }
        int GenericParameterPosition { get; }
        bool HasClassConstraint { get; }
        bool HasStructConstraint { get; }
        bool HasNewConstraint { get; }
        bool HasOutConstraint { get; }
        bool HasInConstraint { get; }

        IEnumerable<object> Members(
            string name,
            object types,
            int arity,
            bool? isPublic,
            bool? isStatic,
            bool declaredOnly,
            bool useExactBinding
        );

        ICSharpTypedConstant DefaultValue { get; }
    }
    internal interface ICSharpMethod : ICSharpMember {
        bool IsConstructor { get; }
        bool IsFinalizer { get; }
        bool IsPropertyGet { get; }
        bool IsPropertySet { get; }
        bool IsEventAdd { get; }
        bool IsEventRemove { get; }
        bool IsAnonymousFunction { get; }
        bool IsDelegateInvoke { get; }
        bool IsOperator { get; }
        bool IsConversion { get; }
        bool IsExplicitInterfaceImplementation { get; }

        bool IsVirtual { get; }
        bool IsAbstract { get; }
        bool IsOverride { get; }
        bool IsSealed { get; }
        bool IsExtern { get; }
        bool IsStatic { get; }

        bool IsGenericMethod { get; }
        bool IsGenericMethodDefinition { get; }
        object GenericMethodDefinition { get; }
        IEnumerable<object> GenericArguments();
        object MakeGenericMethod(IEnumerable<object> genericArguments);

        IEnumerable<object> Parameters();
        object ReturnType { get; }
    }
    internal interface ICSharpParameter : ICSharpSymbol {
        int Position { get; }
        object Type { get; }
        object DeclaringType { get; }
        object DeclaringMethod { get; }
        object DeclaringProperty { get; }
        bool HasDefaultValue { get; }
        bool IsExtern { get; }
        bool IsOptional { get; }
        bool IsParams { get; }
        bool IsThis { get; }
        bool IsOut { get; }
        bool IsRef { get; }

        dynamic DefaultValue { get; }
    }
    internal interface ICSharpDataMember : ICSharpMember {
    }
    internal interface ICSharpField : ICSharpDataMember {
        object Type { get; }
        bool HasConstantValue { get; }
        bool IsConst { get; }
        bool IsReadOnly { get; }

        ICSharpTypedConstant ConstantValue { get; }
    }
    internal interface ICSharpProperty : ICSharpDataMember {
        object Type { get; }

        bool IsAbstract { get; }
        bool IsIndexer { get; }
        bool IsExtern { get; }
        bool IsOverride { get; }
        bool IsSealed { get; }
        bool IsStatic { get; }
        bool IsVirtual { get; }

        bool IsReadOnly { get; }
        bool IsWriteOnly { get; }

        object GetMethod { get; }
        object SetMethod { get; }
        object OverriddenProperty { get; }

        IEnumerable<object> Parameters();
    }
    internal interface ICSharpEvent : ICSharpMember {
    }
    internal interface ICSharpAttribute {
        object Type { get; }
        object Constructor { get; }
        IEnumerable<KeyValuePair<object, ICSharpTypedConstant>> NamedArguments();
        IEnumerable<ICSharpTypedConstant> Arguments();
    }
    internal interface ICSharpTypedConstant {
        bool IsString { get; }
        bool IsCharacter { get; }
        bool IsPrimitive { get; }
        bool IsEnum { get; }
        bool IsType { get; }
        bool IsArray { get; }

        object Type { get; }
        object Value { get; }
        IEnumerable<ICSharpTypedConstant> Values();
    }

    internal abstract class ReflectionSymbol : ICSharpSymbol {
        private object m_symbol;

        internal ReflectionSymbol(object symbol) {
            m_symbol = symbol;
        }

        internal object Symbol {
            get { return m_symbol; }
        }

        public string Name {
            get {
                if (m_symbol is Assembly)
                    return ((Assembly)m_symbol).GetName().Name;

                if (m_symbol is ParameterInfo)
                    return ((ParameterInfo)m_symbol).Name;

                return ((MemberInfo)m_symbol).Name;
            }
        }

        public override string ToString() {
            return Name;
        }
        public object Object {
            get { return m_symbol; }
        }
        public IEnumerable<ICSharpAttribute> AttributeData(CSharpType type, bool declaredOnly) {
            IEnumerable<CustomAttributeData> data;

            if (Symbol is ParameterInfo)
                data = CustomAttributeData.GetCustomAttributes((ParameterInfo)Symbol);

            else if (Symbol is Assembly)
                data = CustomAttributeData.GetCustomAttributes((Assembly)Symbol);

            else
                data = CustomAttributeData.GetCustomAttributes((MemberInfo)Symbol);

            if (type != null)
                data = data.Where(o => o.Constructor.DeclaringType.IsSubclassOfOrIs((Type)type.Object));

            return data.Select(o => new ReflectionAttribute(o));
        }
    }
    internal sealed class ReflectionAssembly : ReflectionSymbol, ICSharpAssembly {

        internal ReflectionAssembly(Assembly assembly)
            : base(assembly) {
        }

        internal Assembly Assembly {
            get { return (Assembly)Symbol; }
        }

        public string Location {
            get { return Assembly.Location; }
        }
        public CSharpAssemblyName AssemblyName {
            get { return Assembly.GetName(); }
        }

        public Assembly Load() {
            return Assembly;
        }
        public IEnumerable<object> Types() {
            return Assembly.GetTypes();
        }
        public object GetType(string name, string ns, int arity) {
            var fullName = new TextJoin(".",
                suffix: new TextJoin(prefix: "`") { arity == 0 ? (int?)null : arity }
            ) { ns, name }.ReadToEnd();

            return Assembly.GetType(fullName);
        }
    }
    internal abstract class ReflectionMember : ReflectionSymbol, ICSharpMember {

        internal ReflectionMember(MemberInfo memberInfo)
            : base(memberInfo) {
        }
        internal MemberInfo MemberInfo {
            get { return (MemberInfo)Symbol; }
        }

        public object DeclaringMember {
            get {
                var type = MemberInfo as Type;
                if (type != null)
                    return (object)type.DeclaringType ??
                        (type.IsGenericParameter ? type.DeclaringMethod : null);

                return MemberInfo.DeclaringType;
            }
        }
        public object DeclaringAssembly {
            get { return MemberInfo.Module.Assembly; }
        }

        public abstract bool IsProtected { get; }
        public abstract bool IsInternal { get; }
        public abstract bool IsPrivate { get; }
        public abstract bool IsPublic { get; }
    }
    internal sealed class ReflectionType : ReflectionMember, ICSharpType {

        internal ReflectionType(Type typeSymbol)
            : base(typeSymbol) {
        }
        internal Type Type {
            get { return (Type)Symbol; }
        }

        public override bool IsProtected {
            get { return Type.IsNestedFamily || Type.IsNestedFamANDAssem || Type.IsNestedFamORAssem; }
        }
        public override bool IsInternal {
            get { return Type.IsNestedAssembly || Type.IsNestedFamANDAssem || Type.IsNestedFamORAssem; }
        }
        public override bool IsPrivate {
            get { return (Type.IsNotPublic && !IsProtected && !IsInternal) || Type.IsNestedPrivate; }
        }
        public override bool IsPublic {
            get { return Type.IsPublic || Type.IsNestedPublic; }
        }

        public object ElementType {
            get { return Type.GetElementType(); }
        }
        public int ArrayRank {
            get { return Type.GetArrayRank(); }
        }
        public string MetadataName {
            get { return Type.Name; }
        }
        public string Namespace {
            get { return Type.Namespace; }
        }
        public object BaseType {
            get { return Type.BaseType; }
        }

        public IEnumerable<object> Interfaces() {
            return Type.GetInterfaces();
        }

        public bool IsAbstract {
            get { return Type.IsAbstract; }
        }
        public bool IsSealed {
            get { return Type.IsSealed; }
        }
        public bool IsStatic {
            get { return Type.IsStatic(); }
        }
        public bool IsPointer {
            get { return Type.IsPointer; }
        }
        public bool IsDelegate {
            get { return Type.IsSubclassOf(typeof(Delegate)); }
        }
        public bool IsArray {
            get { return Type.IsArray; }
        }
        public bool IsEnum {
            get { return Type.IsEnum; }
        }
        public bool IsStruct {
            get { return Type.IsStruct(); }
        }
        public bool IsClass {
            get { return Type.IsClass; }
        }
        public bool IsInterface {
            get { return Type.IsInterface; }
        }
        public bool IsReferenceType {
            get { return !Type.IsValueType; }
        }
        public bool IsValueType {
            get { return Type.IsValueType; }
        }

        public bool IsSubclassOf(ICSharpType type) {
            return Type.IsSubclassOf((Type)type.Object);
        }

        public int GenericArity {
            get { return Type.GetGenericArguments().Count(); }
        }
        public object GenericTypeDefinition {
            get {
                if (!Type.IsGenericType)
                    return null;
                return Type.GetGenericTypeDefinition(); 
            }
        }
        public IEnumerable<object> GenericArguments() {
            return Type.GetGenericArguments();
        }
        public object MakeGenericType(IEnumerable<object> enumerable) {
            return Type.MakeGenericType(enumerable.Cast<Type>().ToArray());
        }
        public IEnumerable<object> GenericParameterConstraints() {
            return Type.GetGenericParameterConstraints();
        }

        public bool IsGenericParameter {
            get { return Type.IsGenericParameter; }
        }
        public int GenericParameterPosition {
            get { return Type.GenericParameterPosition; }
        }
        public bool HasClassConstraint {
            get { return (Type.GenericParameterAttributes & GenericParameterAttributes.ReferenceTypeConstraint) != 0; }
        }
        public bool HasStructConstraint {
            get { return (Type.GenericParameterAttributes & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0; }
        }
        public bool HasNewConstraint {
            get { return (Type.GenericParameterAttributes & GenericParameterAttributes.DefaultConstructorConstraint) != 0; }
        }
        public bool HasOutConstraint {
            get { return (Type.GenericParameterAttributes & GenericParameterAttributes.Contravariant) != 0; }
        }
        public bool HasInConstraint {
            get { return (Type.GenericParameterAttributes & GenericParameterAttributes.Covariant) != 0; }
        }

        public object MakePointerType() {
            return Type.MakePointerType();
        }
        public object MakeArrayType(int? rank = null) {
            if (rank == null)
                return Type.MakeArrayType();
            return Type.MakeArrayType((int)rank);
        }
        public object MakeReferenceType() {
            return Type.MakeByRefType();
        }

        public IEnumerable<object> Members(
            string name,
            object types,
            int arity,
            bool? isPublic,
            bool? isStatic,
            bool declaredOnly,
            bool useExactBinding) {

            return Type.Get<MemberInfo>(
                name: name,
                types: types,

                isPublic: isPublic == null || isPublic == true,
                isNonPublic: isPublic == null || isPublic == false,

                isInstance: isStatic == null || isStatic == false,
                isStatic: isStatic == null || isStatic == true,

                declaredOnly: declaredOnly,

                useExactBinding: useExactBinding

            // filter out all special names
            ).Where(o => {
                var field = o as FieldInfo;
                if (field != null)
                    return (field.Attributes & FieldAttributes.RTSpecialName) == 0;

                var method = o as MethodInfo;
                if (method != null)
                    return (method.Attributes & MethodAttributes.RTSpecialName) == 0;

                return true;
            }).ToArray();
        }

        public ICSharpTypedConstant DefaultValue {
            get { return new ReflectionTypedConstant(Type, Type.GetDefaultValue()); }
        }
    }
    internal sealed class ReflectionMethod : ReflectionMember, ICSharpMethod {

        internal ReflectionMethod(MethodBase methodSymbol)
            : base(methodSymbol) {
        }
        internal MethodBase Method {
            get { return (MethodBase)Symbol; }
        }

        public override bool IsProtected {
            get { return Method.IsFamily || Method.IsFamilyAndAssembly || Method.IsFamilyOrAssembly; }
        }
        public override bool IsInternal {
            get { return Method.IsAssembly || Method.IsFamilyAndAssembly || Method.IsFamilyOrAssembly; }
        }
        public override bool IsPrivate {
            get { return Method.IsPrivate; }
        }
        public override bool IsPublic {
            get { return Method.IsPublic; }
        }

        public bool IsConstructor {
            get { return Method is ConstructorInfo; }
        }
        public bool IsFinalizer {
            get { throw new NotImplementedException(); }
        }
        public bool IsPropertyGet {
            get { throw new NotImplementedException(); }
        }
        public bool IsPropertySet {
            get { throw new NotImplementedException(); }
        }
        public bool IsEventAdd {
            get { throw new NotImplementedException(); }
        }
        public bool IsEventRemove {
            get { throw new NotImplementedException(); }
        }
        public bool IsAnonymousFunction {
            get { throw new NotImplementedException(); }
        }
        public bool IsDelegateInvoke {
            get { throw new NotImplementedException(); }
        }
        public bool IsOperator {
            get { throw new NotImplementedException(); }
        }
        public bool IsConversion {
            get { throw new NotImplementedException(); }
        }
        public bool IsVirtual {
            get { return Method.IsVirtual; }
        }
        public bool IsAbstract {
            get { return Method.IsAbstract; }
        }
        public bool IsOverride {
            get { throw new NotImplementedException(); }
        }
        public bool IsSealed {
            get { return Method.IsFinal; }
        }
        public bool IsExtern {
            get { throw new NotImplementedException(); }
        }
        public bool IsStatic {
            get { return Method.IsStatic; }
        }
        public bool IsExplicitInterfaceImplementation {
            get { throw new NotImplementedException(); }
        }

        public bool IsGenericMethod {
            get { throw new NotImplementedException(); }
        }
        public bool IsGenericMethodDefinition {
            get { throw new NotImplementedException(); }
        }
        public object GenericMethodDefinition {
            get { throw new NotImplementedException(); }
        }
        public IEnumerable<object> GenericArguments() {
            throw new NotImplementedException();
        }
        public object MakeGenericMethod(IEnumerable<object> genericArguments) {
            throw new NotImplementedException();
        }

        public IEnumerable<object> Parameters() {
            return Method.GetParameters();
        }
        public object ReturnType {
            get {
                if (Method is ConstructorInfo)
                    return null;
                return ((MethodInfo)Method).ReturnType;
            }
        }
    }
    internal sealed class ReflectionParameter : ReflectionSymbol, ICSharpParameter {
        internal ReflectionParameter(ParameterInfo parameterSymbol)
            : base(parameterSymbol) {
        }

        public int Position {
            get { throw new NotImplementedException(); }
        }
        public object Type {
            get { throw new NotImplementedException(); }
        }
        public object DeclaringType {
            get { throw new NotImplementedException(); }
        }
        public object DeclaringMethod {
            get { throw new NotImplementedException(); }
        }
        public object DeclaringProperty {
            get { throw new NotImplementedException(); }
        }

        public bool HasDefaultValue {
            get { throw new NotImplementedException(); }
        }
        public bool IsExtern {
            get { throw new NotImplementedException(); }
        }
        public bool IsOptional {
            get { throw new NotImplementedException(); }
        }
        public bool IsParams {
            get { throw new NotImplementedException(); }
        }
        public bool IsThis {
            get { throw new NotImplementedException(); }
        }
        public bool IsOut {
            get { throw new NotImplementedException(); }
        }
        public bool IsRef {
            get { throw new NotImplementedException(); }
        }

        public dynamic DefaultValue {
            get { throw new NotImplementedException(); }
        }
    }
    internal sealed class ReflectionField : ReflectionMember, ICSharpField {
        internal ReflectionField(FieldInfo fieldSymbol)
            : base(fieldSymbol) {
        }
        internal FieldInfo Field {
            get { return (FieldInfo)Symbol; }
        }

        public override bool IsProtected {
            get { return Field.IsFamily || Field.IsFamilyAndAssembly || Field.IsFamilyOrAssembly; }
        }
        public override bool IsInternal {
            get { return Field.IsAssembly || Field.IsFamilyAndAssembly || Field.IsFamilyOrAssembly; }
        }
        public override bool IsPrivate {
            get { return Field.IsPrivate; }
        }
        public override bool IsPublic {
            get { return Field.IsPublic; }
        }

        public object Type {
            get { return Field.FieldType; }
        }

        public bool HasConstantValue {
            get { return (Field.Attributes & FieldAttributes.HasDefault) != 0; }
        }
        public bool IsConst {
            get { return (Field.Attributes & FieldAttributes.Literal) != 0; }
        }
        public bool IsReadOnly {
            get { return (Field.Attributes & FieldAttributes.InitOnly) != 0; }
        }

        public ICSharpTypedConstant ConstantValue {
            get {
                return HasConstantValue ?
                    new ReflectionTypedConstant(
                        Field.FieldType,
                        Field.GetRawConstantValue()
                    ) : null;
            }
        }
    }
    internal sealed class ReflectionProperty : ReflectionMember, ICSharpProperty {
        internal ReflectionProperty(PropertyInfo propertySymbol)
            : base(propertySymbol) {
        }
        internal PropertyInfo Property {
            get { return (PropertyInfo)Symbol; }
        }

        internal MethodInfo GetOrSetMethod {
            get { return Property.GetGetMethod(true) ?? Property.GetSetMethod(true); }
        }

        public override bool IsProtected {
            get { return GetOrSetMethod.IsFamily || GetOrSetMethod.IsFamilyAndAssembly || GetOrSetMethod.IsFamilyOrAssembly; }
        }
        public override bool IsInternal {
            get { return GetOrSetMethod.IsAssembly || GetOrSetMethod.IsFamilyAndAssembly || GetOrSetMethod.IsFamilyOrAssembly; }
        }
        public override bool IsPrivate {
            get { return GetOrSetMethod.IsPrivate; }
        }
        public override bool IsPublic {
            get { return GetOrSetMethod.IsPublic; }
        }

        public object Type {
            get { return Property.PropertyType; }
        }

        public bool IsAbstract {
            get { return Property.IsAbstract(); }
        }
        public bool IsIndexer {
            get { return Property.GetParameters().Any(); }
        }
        public bool IsExtern {
            get { throw new NotImplementedException(); }
        }
        public bool IsOverride {
            get { return (Property.GetGetMethod(true) ?? Property.GetSetMethod(true)).IsOverride(); }
        }
        public bool IsSealed {
            get { return Property.IsFinalVirtual(); }
        }
        public bool IsStatic {
            get { return Property.IsStatic(); }
        }
        public bool IsVirtual {
            get { return Property.IsVirtual(); }
        }
        public bool IsReadOnly {
            get { return Property.GetSetMethod(true) == null; }
        }
        public bool IsWriteOnly {
            get { return Property.GetGetMethod(true) == null; }
        }

        public object GetMethod {
            get { return Property.GetGetMethod(); }
        }
        public object SetMethod {
            get { return Property.GetSetMethod(); }
        }
        public object OverriddenProperty {
            get { return (Property.GetGetMethod(true) ?? Property.GetSetMethod(true)).GetOverrides().First().GetProperty(); }
        }

        public IEnumerable<object> Parameters() {
            return Property.GetParameters();
        }
    }
    internal sealed class ReflectionEvent : ReflectionMember, ICSharpEvent {
        internal ReflectionEvent(EventInfo eventSymbol)
            : base(eventSymbol) {
        }
        internal EventInfo Event {
            get { return (EventInfo)Symbol; }
        }

        internal MethodInfo AddOrRemoveMethod {
            get { return Event.GetAddMethod(true) ?? Event.GetRemoveMethod(true); }
        }

        public override bool IsProtected {
            get { return AddOrRemoveMethod.IsFamily || AddOrRemoveMethod.IsFamilyAndAssembly || AddOrRemoveMethod.IsFamilyOrAssembly; }
        }
        public override bool IsInternal {
            get { return AddOrRemoveMethod.IsAssembly || AddOrRemoveMethod.IsFamilyAndAssembly || AddOrRemoveMethod.IsFamilyOrAssembly; }
        }
        public override bool IsPrivate {
            get { return AddOrRemoveMethod.IsPrivate; }
        }
        public override bool IsPublic {
            get { return AddOrRemoveMethod.IsPublic; }
        }
    }
    internal sealed class ReflectionAttribute : ICSharpAttribute {
        private CustomAttributeData m_data;

        internal ReflectionAttribute(CustomAttributeData data) {
            m_data = data;
        }

        public object Type {
            get { return m_data.Constructor.DeclaringType; }
        }
        public object Constructor {
            get { return m_data.Constructor; }
        }
        public IEnumerable<KeyValuePair<object, ICSharpTypedConstant>> NamedArguments() {
            return m_data.NamedArguments.Select(o =>
                new KeyValuePair<object, ICSharpTypedConstant>(
                    m_data.AttributeType.Get<MemberInfo>(o.MemberName, isInstance: true).Single(),
                    new ReflectionTypedConstant(o.TypedValue)
                )
            );
        }
        public IEnumerable<ICSharpTypedConstant> Arguments() {
            return m_data.ConstructorArguments.Select(o =>
                new ReflectionTypedConstant(o)
            );
        }
    }
    internal sealed class ReflectionTypedConstant : ICSharpTypedConstant {
        private Type m_type;
        private object m_value;

        internal ReflectionTypedConstant(Type type, object value) {
            m_type = type;
            m_value = value;
        }
        internal ReflectionTypedConstant(CustomAttributeTypedArgument argument)
            : this(argument.ArgumentType, argument.Value) {
        }

        public bool IsString {
            get { return m_type.FullName == typeof(string).FullName; }
        }
        public bool IsCharacter {
            get { return m_type.FullName == typeof(char).FullName; }
        }
        public bool IsPrimitive {
            get { return m_type.IsPrimitive; }
        }
        public bool IsEnum {
            get { return m_type.IsEnum; }
        }
        public bool IsType {
            get { return m_type.FullName == typeof(Type).FullName; }
        }
        public bool IsArray {
            get { return m_type.IsArray; }
        }

        public object Type {
            get { return m_type; }
        }
        public object Value {
            get {
                if (IsArray)
                    return Values();

                var typeSymbol = m_value as NamedTypeSymbol;
                if (typeSymbol != null)
                    return typeSymbol;

                return m_value;
            }
        }
        public IEnumerable<ICSharpTypedConstant> Values() {
            if (!IsArray)
                throw new InvalidOperationException();

            return m_value.ToEnumerable<CustomAttributeTypedArgument>()
                .Select(o => new ReflectionTypedConstant(o));
        }
    }

    internal abstract class RoslynSymbol : ICSharpSymbol {
        private Symbol m_symbol;

        internal RoslynSymbol(Symbol symbol) {
            m_symbol = symbol;
        }

        internal Symbol Symbol {
            get { return m_symbol; }
        }

        public string Name {
            get { return m_symbol.Name; }
        }
        public object Object {
            get { return m_symbol; }
        }
        public IEnumerable<ICSharpAttribute> AttributeData(CSharpType type, bool declaredOnly) {
            throw new NotImplementedException();
        }
    }
    internal sealed class RoslynAssembly : RoslynSymbol, ICSharpAssembly {
        internal RoslynAssembly(AssemblySymbol assemblySymbol)
            : base(assemblySymbol) {
        }

        internal AssemblySymbol AssemblySymbol {
            get { return (AssemblySymbol)Symbol; }
        }

        public string Location {
            get { return AssemblySymbol.Identity.Location; }
        }
        public CSharpAssemblyName AssemblyName {
            get {
                if (!AssemblySymbol.Identity.IsStrongName)
                    return null;
                return AssemblySymbol.Identity.ToAssemblyName();
            }
        }

        public Assembly Load() {
            throw new InvalidOperationException();
        }
        public IEnumerable<object> Types() {
            var types = new List<TypeSymbol>();

            // get all non-nested types
            var nsStack = new Stack<NamespaceSymbol>();
            nsStack.Push(AssemblySymbol.GlobalNamespace);
            while (nsStack.Count > 0) {
                var ns = nsStack.Pop();
                types.AddRange(ns.GetTypeMembers());
                nsStack.PushRange(ns.GetNamespaceMembers());
            }

            // add nested types too
            var typeStack = new Stack<TypeSymbol>();
            typeStack.PushRange(types);
            types.Clear();
            while (typeStack.Count > 0) {
                var type = typeStack.Pop();
                types.Add(type);
                typeStack.PushRange(type.GetTypeMembers().OfType<TypeSymbol>());
            }

            return types;
        }
        public object GetType(string name, string ns, int arity) {

            // dig through namespaces
            var namespaceSymbol = AssemblySymbol.GlobalNamespace;
            foreach (var nsPart in name.StringSplit('.')) {
                namespaceSymbol = namespaceSymbol.GetNamespaceMembers().FirstOrDefault(o => o.Name == nsPart);
                if (namespaceSymbol == null)
                    return null;
            }

            return namespaceSymbol.GetTypeMembers(name, arity).SingleOrDefault();
        }
    }
    internal abstract class RoslynMember : RoslynSymbol, ICSharpMember {

        public RoslynMember(Symbol symbol)
            : base(symbol) {
        }
        public object DeclaringMember {
            get { return Symbol.ContainingSymbol; }
        }
        public object DeclaringAssembly {
            get { return Symbol.ContainingAssembly; }
        }

        public bool IsProtected {
            get {
                return Symbol.DeclaredAccessibility == Accessibility.Protected ||
                    Symbol.DeclaredAccessibility == Accessibility.ProtectedAndInternal ||
                    Symbol.DeclaredAccessibility == Accessibility.ProtectedInternal;
            }
        }
        public bool IsInternal {
            get {
                return Symbol.DeclaredAccessibility == Accessibility.Internal ||
                    Symbol.DeclaredAccessibility == Accessibility.ProtectedAndInternal ||
                    Symbol.DeclaredAccessibility == Accessibility.ProtectedInternal;
            }
        }
        public bool IsPrivate {
            get {
                return !IsProtected && !IsInternal && !IsPublic;
            }
        }
        public bool IsPublic {
            get { return Symbol.DeclaredAccessibility == Accessibility.Public; }
        }
    }
    internal sealed class RoslynType : RoslynMember, ICSharpType {
        public RoslynType(TypeSymbol typeSymbol)
            : base(typeSymbol) {
        }

        internal TypeSymbol TypeSymbol {
            get { return (TypeSymbol)Symbol; }
        }
        internal TypeParameterSymbol TypeParameterSymbol {
            get { return (TypeParameterSymbol)Symbol; }
        }
        internal NamedTypeSymbol NamedTypeSymbol {
            get { return (NamedTypeSymbol)Symbol; }
        }
        internal PointerTypeSymbol PointerTypeSymbol {
            get { return (PointerTypeSymbol)Symbol; }
        }
        internal ArrayTypeSymbol ArrayTypeSymbol {
            get { return (ArrayTypeSymbol)Symbol; }
        }

        #region Identity
        public object BaseType {
            get {
                if (IsGenericParameter) {
                    if (HasStructConstraint)
                        return typeof(ValueType);

                    var classConstraint = GenericParameterConstraints()
                        .Cast<TypeSymbol>()
                        .FirstOrDefault(o => o.TypeKind == TypeKind.Class);

                    if (classConstraint != null)
                        return classConstraint;

                    return typeof(object);
                }

                return TypeSymbol.BaseType;
            }
        }
        public IEnumerable<object> Interfaces() {
            return TypeSymbol.AllInterfaces.Select(o => (object)o);
        }
        public Type RuntimeType {
            get { throw new NotImplementedException(); }
        }
        #endregion

        #region ElementType
        public object ElementType {
            get {
                if (IsArray)
                    return ArrayTypeSymbol.ElementType;

                if (IsPointer)
                    return PointerTypeSymbol.PointedAtType;

                return null;
            }
        }
        public int ArrayRank {
            get {
                if (!IsArray)
                    throw new InvalidOperationException();

                return ArrayTypeSymbol.Rank;
            }
        }
        #endregion

        #region Name
        public string MetadataName {
            get { return TypeSymbol.MetadataName; }
        }
        public string Namespace {
            get {

                var elementType = TypeSymbol;
                while (elementType.Kind == SymbolKind.ArrayType ||
                    elementType.Kind == SymbolKind.PointerType) {

                    if (elementType.Kind == SymbolKind.ArrayType)
                        elementType = ((ArrayTypeSymbol)elementType).ElementType;

                    if (elementType.Kind == SymbolKind.PointerType)
                        elementType = ((PointerTypeSymbol)elementType).PointedAtType;
                }

                var ns = Symbol.ContainingNamespace;
                if (ns == null)
                    return null;

                var containingNamespaces =
                    ns.ToLinkList(o => o.ContainingNamespace).Reverse().Skip(1).ToArray();

                return containingNamespaces.StringJoin(".", o => o.Name);
            }
        }
        #endregion

        #region Modifiers
        public bool IsAbstract {
            get { return TypeSymbol.IsAbstract; }
        }
        public bool IsSealed {
            get {
                if (IsArray)
                    return true;

                return TypeSymbol.IsSealed;
            }
        }
        public bool IsStatic {
            get { return TypeSymbol.IsStatic; }
        }
        #endregion

        #region Type of Type
        public bool IsValueType {
            get { return TypeSymbol.IsValueType; }
        }
        public bool IsReferenceType {
            get {
                if (IsGenericParameter && GenericParameterConstraints()
                    .Cast<TypeSymbol>().Any(o => o.TypeKind == TypeKind.Class))
                    return true;

                return TypeSymbol.IsReferenceType;
            }
        }

        public bool IsInterface {
            get { return TypeSymbol.TypeKind == TypeKind.Interface; }
        }
        public bool IsClass {
            get { return TypeSymbol.TypeKind == TypeKind.Class; }
        }
        public bool IsStruct {
            get { return TypeSymbol.TypeKind == TypeKind.Struct; }
        }
        public bool IsEnum {
            get { return TypeSymbol.TypeKind == TypeKind.Enum; }
        }
        public bool IsArray {
            get { return TypeSymbol.TypeKind == TypeKind.ArrayType; }
        }
        public bool IsDelegate {
            get { return TypeSymbol.TypeKind == TypeKind.Delegate; }
        }
        public bool IsPointer {
            get { return TypeSymbol.TypeKind == TypeKind.PointerType; }
        }
        #endregion

        #region Generic Type
        public int GenericArity {
            get {
                if (!IsGenericType)
                    return 0;

                return NamedTypeSymbol.Arity;
            }
        }
        public bool IsGenericType {
            get { return GenericArguments().Any(); }
        }
        public bool IsGenericTypeDefinition {
            get { return IsGenericType && TypeSymbol == GenericTypeDefinition; }
        }
        public object GenericTypeDefinition {
            get {
                if (!IsGenericType)
                    return Symbol;
                return Symbol.OriginalDefinition;
            }
        }
        public IEnumerable<object> GenericArguments() {
            if (!(Symbol is NamedTypeSymbol))
                return Enumerable.Empty<object>();

            return NamedTypeSymbol.TypeArguments.Select(o => (object)o);
        }
        public CSharpType MakeGenericType(IEnumerable<CSharpType> genericArguments) {
            return MakeGenericType(genericArguments.ToArray());
        }
        public object MakeGenericType(IEnumerable<object> genericArguments) {
            if (!IsGenericTypeDefinition)
                throw new InvalidOperationException();

            var arguments = genericArguments.Select(o => (TypeSymbol)o);

            return NamedTypeSymbol.Construct(arguments.ToArray());
        }
        #endregion

        #region Generic Parameter
        public bool IsGenericParameter {
            get { return Symbol is TypeParameterSymbol; }
        }
        public int GenericParameterPosition {
            get {
                if (!IsGenericParameter)
                    throw new InvalidOperationException();

                return TypeParameterSymbol.DeclaringType.TypeParameters.IndexOf(TypeParameterSymbol);
            }
        }
        public IEnumerable<object> GenericParameterConstraints() {
            if (!IsGenericParameter)
                return Enumerable.Empty<object>();

            return TypeParameterSymbol.ConstraintTypes.Select(o => (object)o);
        }
        public bool HasClassConstraint {
            get {
                if (!IsGenericParameter)
                    return false;

                return TypeParameterSymbol.HasReferenceTypeConstraint;
            }
        }
        public bool HasStructConstraint {
            get {
                if (!IsGenericParameter)
                    return false;

                return TypeParameterSymbol.HasValueTypeConstraint;
            }
        }
        public bool HasNewConstraint {
            get {
                if (!IsGenericParameter)
                    return false;

                return TypeParameterSymbol.HasConstructorConstraint;
            }
        }
        public bool HasOutConstraint {
            get {
                if (!IsGenericParameter)
                    return false;

                return TypeParameterSymbol.Variance == VarianceKind.Out;
            }
        }
        public bool HasInConstraint {
            get {
                if (!IsGenericParameter)
                    return false;

                return TypeParameterSymbol.Variance == VarianceKind.In;
            }
        }
        #endregion

        public object MakePointerType() {
            throw new NotImplementedException();
        }
        public object MakeArrayType(int? rank = null) {
            throw new NotImplementedException();
        }
        public object MakeReferenceType() {
            throw new NotImplementedException();
        }

        public bool IsSubclassOf(ICSharpType type) {
            return TypeSymbol.ToLinkList(o => o.BaseType).Any(o => o == type.Object);
        }

        public IEnumerable<object> Members(
            string name = null,
            object types = null,
            int arity = 0,
            bool? isPublic = null,
            bool? isStatic = null,
            bool declaredOnly = false,
            bool useExactBinding = false) {

            if (IsGenericParameter)
                return Enumerable.Empty<CSharpMember>();

            var members =
                from o in name == null ? NamedTypeSymbol.GetMembers() : NamedTypeSymbol.GetMembers(name)
                where name == null || o.Name == name
                let type = o as NamedTypeSymbol where type == null || type.Arity == arity
                let method = o as MethodSymbol where method == null || method.Arity == arity
                where isStatic == null || isStatic == o.IsStatic
                where isPublic == null || (o.DeclaredAccessibility == Accessibility.Public) == isPublic
                select o;

            return members;
        }

        public ICSharpTypedConstant DefaultValue {
            get { throw new NotImplementedException(); }
        }
    }
    internal sealed class RoslynMethod : RoslynMember, ICSharpMethod {
        public RoslynMethod(MethodSymbol methodSymbol)
            : base(methodSymbol) {
        }

        private MethodSymbol MethodSymbol {
            get { return (MethodSymbol)Symbol; }
        }

        #region Kind
        public bool IsConstructor {
            get { return MethodSymbol.MethodKind == MethodKind.Constructor || MethodSymbol.MethodKind == MethodKind.StaticConstructor; }
        }
        public bool IsFinalizer {
            get { return MethodSymbol.MethodKind == MethodKind.Destructor; }
        }

        public bool IsPropertyGet {
            get { return MethodSymbol.MethodKind == MethodKind.PropertyGet; }
        }
        public bool IsPropertySet {
            get { return MethodSymbol.MethodKind == MethodKind.PropertySet; }
        }
        public bool IsEventAdd {
            get { return MethodSymbol.MethodKind == MethodKind.EventAdd; }
        }
        public bool IsEventRemove {
            get { return MethodSymbol.MethodKind == MethodKind.EventRemove; }
        }

        public bool IsAnonymousFunction {
            get { return MethodSymbol.MethodKind == MethodKind.AnonymousFunction; }
        }
        public bool IsExplicitInterfaceImplementation {
            get { return MethodSymbol.MethodKind == MethodKind.ExplicitInterfaceImplementation; }
        }
        public bool IsDelegateInvoke {
            get { return MethodSymbol.MethodKind == MethodKind.DelegateInvoke; }
        }

        public bool IsOperator {
            get { return MethodSymbol.MethodKind == MethodKind.Operator; }
        }
        public bool IsConversion {
            get { return MethodSymbol.MethodKind == MethodKind.Conversion; }
        }
        #endregion

        #region Modifiers
        public bool IsVirtual {
            get { return MethodSymbol.IsVirtual; }
        }
        public bool IsAbstract {
            get { return MethodSymbol.IsAbstract; }
        }
        public bool IsOverride {
            get { return MethodSymbol.IsOverride; }
        }
        public bool IsSealed {
            get { return MethodSymbol.IsSealed; }
        }
        public bool IsExtern {
            get { return MethodSymbol.IsExtern; }
        }
        public bool IsStatic {
            get { return MethodSymbol.IsStatic; }
        }
        #endregion

        #region Generic
        public bool IsGenericMethod {
            get { return MethodSymbol.IsGenericMethod; }
        }
        public bool IsGenericMethodDefinition {
            get {
                return MethodSymbol.TypeParameters.ToEnumerable().Zip(
                    MethodSymbol.TypeArguments.ToEnumerable(), (l, r) => l.Equals(r))
                    .All(o => true);
            }
        }
        public object GenericMethodDefinition {
            get { return MethodSymbol.ConstructedFrom; }
        }
        public IEnumerable<object> GenericArguments() {
            return MethodSymbol.TypeArguments.Select(o => (object)o);
        }
        public object MakeGenericMethod(IEnumerable<object> genericArguments) {
            var arguments = genericArguments.Select(o => (TypeSymbol)o);
            var readOnlyArguments = ReadOnlyArray<TypeSymbol>.CreateFrom(arguments.ToArray());
            return MethodSymbol.Construct(readOnlyArguments);
        }
        #endregion

        public IEnumerable<object> Parameters() {
            return MethodSymbol.Parameters.Select(o => (object)o);
        }
        public object ReturnType {
            get { return MethodSymbol.ReturnType; }
        }
    }
    internal sealed class RoslynParameter : RoslynSymbol, ICSharpParameter {
        public RoslynParameter(ParameterSymbol parameterSymbol)
            : base(parameterSymbol) {
        }

        private ParameterSymbol ParameterSymbol {
            get { return (ParameterSymbol)Symbol; }
        }

        public int Position {
            get { return ParameterSymbol.Ordinal; }
        }
        public object Type {
            get { return ParameterSymbol.Type; }
        }

        public object DeclaringType {
            get { return ParameterSymbol.ContainingType; }
        }
        public object DeclaringMethod {
            get { return ParameterSymbol.ContainingSymbol; }
        }
        public object DeclaringProperty {
            get { return ParameterSymbol.ContainingSymbol; }
        }

        public dynamic DefaultValue {
            get {
                var result = ParameterSymbol.DefaultValue;
                if (result == null)
                    return result;

                return result;
            }
        }
        public bool HasDefaultValue {
            get { return ParameterSymbol.HasDefaultValue; }
        }

        public bool IsExtern {
            get { return ParameterSymbol.IsExtern; }
        }
        public bool IsOptional {
            get { return ParameterSymbol.IsOptional; }
        }
        public bool IsParams {
            get { return ParameterSymbol.IsParams; }
        }
        public bool IsThis {
            get { return ParameterSymbol.IsThis; }
        }
        public bool IsOut {
            get { return ParameterSymbol.RefKind == RefKind.Out; }
        }
        public bool IsRef {
            get { return ParameterSymbol.RefKind == RefKind.Ref; }
        }
    }
    internal sealed class RoslynField : RoslynMember, ICSharpField {
        public RoslynField(FieldSymbol fieldSymbol)
            : base(fieldSymbol) {
        }

        private FieldSymbol FieldSymbol {
            get { return (FieldSymbol)Symbol; }
        }

        internal dynamic RawConstantValue {
            get { return FieldSymbol.ConstantValue; }
        }

        public object Type {
            get { return FieldSymbol.Type; }
        }

        public ICSharpTypedConstant ConstantValue {
            get {
                //var result = RawConstantValue;
                //if (result == null)
                //    return result;

                //return result;
                throw new NotImplementedException();
            }
        }
        public bool HasConstantValue {
            get { return FieldSymbol.HasConstantValue; }
        }
        public bool IsConst {
            get { return FieldSymbol.IsConst; }
        }
        public bool IsReadOnly {
            get { return FieldSymbol.IsReadOnly; }
        }
    }
    internal sealed class RoslynProperty : RoslynMember, ICSharpProperty {
        public RoslynProperty(PropertySymbol propertySymbol)
            : base(propertySymbol) {
        }
        private PropertySymbol PropertySymbol {
            get { return (PropertySymbol)Symbol; }
        }

        public object Type {
            get { return PropertySymbol.Type; }
        }

        public bool IsAbstract {
            get { return PropertySymbol.IsAbstract; }
        }
        public bool IsExtern {
            get { return PropertySymbol.IsExtern; }
        }
        public bool IsOverride {
            get { return PropertySymbol.IsOverride; }
        }
        public bool IsSealed {
            get { return PropertySymbol.IsSealed; }
        }
        public bool IsStatic {
            get { return PropertySymbol.IsStatic; }
        }
        public bool IsVirtual {
            get { return PropertySymbol.IsVirtual; }
        }

        public object OverriddenProperty {
            get { return PropertySymbol.OverriddenProperty; }
        }

        public bool IsReadOnly {
            get { return PropertySymbol.IsReadOnly; }
        }
        public bool IsWriteOnly {
            get { return PropertySymbol.IsWriteOnly; }
        }

        public object GetMethod {
            get { return PropertySymbol.GetMethod; }
        }
        public object SetMethod {
            get { return PropertySymbol.SetMethod; }
        }

        public bool IsIndexer {
            get { return PropertySymbol.IsIndexer; }
        }
        public IEnumerable<object> Parameters() {
            return PropertySymbol.Parameters.Select(o => (object)o);
        }
    }
    internal sealed class RoslynEvent : RoslynMember, ICSharpEvent {
        public RoslynEvent(EventSymbol eventSymbol)
            : base(eventSymbol) {
        }
    }
    internal sealed class RoslynAttribute : ICSharpAttribute {

        private AttributeData m_data;
        private CSharpSyntheticAttribute m_value;

        internal RoslynAttribute(AttributeData data) {
            m_data = data;
        }

        public object Type {
            get { return m_data.AttributeClass; }
        }
        public object Constructor {
            get { return m_data.AttributeConstructor; }
        }
        public IEnumerable<KeyValuePair<object, ICSharpTypedConstant>> NamedArguments() {
            return m_data.NamedArguments.Select(o =>
                new KeyValuePair<object, ICSharpTypedConstant>(
                    o.Key,
                    new RoslynTypedConstant(o.Value)
                )
            );
        }
        public IEnumerable<ICSharpTypedConstant> Arguments() {
            return m_data.ConstructorArguments.Select(o => new RoslynTypedConstant(o));
        }
    }
    internal sealed class RoslynTypedConstant : ICSharpTypedConstant {
        private TypedConstant m_value;
        private object m_symbol;

        internal RoslynTypedConstant(TypedConstant value) {
            m_value = value;
        }

        public bool IsString {
            get { return Value is string; }
        }
        public bool IsCharacter {
            get { return Value is char; }
        }
        public bool IsPrimitive {
            get { return m_value.Kind == TypedConstantKind.Primitive && !IsString; }
        }
        public bool IsEnum {
            get { return m_value.Kind == TypedConstantKind.Enum; }
        }
        public bool IsType {
            get { return m_value.Kind == TypedConstantKind.Type; }
        }
        public bool IsArray {
            get { return m_value.Kind == TypedConstantKind.Array; }
        }

        public object Type {
            get { return m_value.Type; }
        }
        public object Value {
            get {
                if (IsArray)
                    return Values();

                var typeSymbol = m_value.Value as NamedTypeSymbol;
                if (typeSymbol != null)
                    return typeSymbol;

                return m_value.Value;
            }
        }
        public IEnumerable<ICSharpTypedConstant> Values() {
            if (!IsArray)
                throw new InvalidOperationException();

            return m_value.Values.Select(o => new RoslynTypedConstant(o));
        }
    }

    public abstract class CSharpLoaderObject {
        private readonly CSharpLoader m_loader;

        internal CSharpLoaderObject(CSharpLoader loader) {
            m_loader = loader;
        }

        public CSharpLoader Loader {
            get { return m_loader; }
        }
    }
    public abstract class CSharpSymbol : CSharpLoaderObject {
        private readonly ICSharpSymbol m_symbol;

        internal CSharpSymbol(CSharpLoader loader, ICSharpSymbol symbol)
            : base(loader) {
            m_symbol = symbol;
        }

        internal object Object {
            get { return m_symbol.Object; }
        }
        internal ICSharpSymbol Symbol {
            get { return m_symbol; }
        }

        public IEnumerable<CSharpAttribute> AttributeData(CSharpType type = null, bool declaredOnly = false) {
            return Symbol.AttributeData(type, declaredOnly).Select(o => new CSharpAttribute(Loader, o));
        }
        public CSharpAttribute GetAttributeData(CSharpType type = null, bool declaredOnly = false) {
            return AttributeData(type, declaredOnly).SingleOrDefault();
        }

        public IEnumerable<dynamic> Attributes(CSharpType type = null, bool declaredOnly = false) {
            return AttributeData(type, declaredOnly).Select(o => new CSharpSyntheticAttribute(o));
        }
        public IEnumerable<dynamic> Attributes(Type type = null, bool declaredOnly = false) {
            return Attributes(Loader.GetType(type), declaredOnly);
        }

        public dynamic GetAttribute(CSharpType type, bool declaredOnly = false) {
            return Attributes(type, declaredOnly).SingleOrDefault();
        }
        public dynamic GetAttribute(Type type, bool declaredOnly = false) {
            return GetAttribute(Loader.GetType(type), declaredOnly);
        }

        public IEnumerable<object> LoadAttributes(Type type = null, bool declaredOnly = false) {
            var attributes = Attributes(Loader.GetType(type), declaredOnly);
            return attributes.Select(o => o.Load());
        }
        public object LoadAttribute(Type type, bool declaredOnly = false) {
            var result = GetAttribute(type, declaredOnly);
            if (result == null)
                return null;
            return result.Load();
        }
        public IEnumerable<T> LoadAttributes<T>(bool declaredOnly = false) {
            return LoadAttributes(typeof(T), declaredOnly).Cast<T>();
        }
        public T LoadAttribute<T>(bool declaredOnly = false) {
            return (T)LoadAttribute(typeof(T), declaredOnly);
        }

        public bool IsDefined(Type type, bool declaredOnly = false) {
            return IsDefined(Loader.GetType(type), declaredOnly);
        }
        public bool IsDefined(CSharpType type, bool declaredOnly = false) {
            return AttributeData(type, declaredOnly).Any();
        }
        public bool IsDefined<T>(bool declaredOnly = false) {
            return AttributeData(Loader.GetType(typeof(T)), declaredOnly).Any();
        }
        public virtual string Name {
            get { return Symbol.Name; }
        }
    }
    public sealed class CSharpAssembly : CSharpSymbol {
        public static bool operator ==(CSharpAssembly lhs, Assembly rhs) {
            if ((object)lhs == null || (object)rhs == null)
                return (object)rhs == (object)lhs;

            return lhs == lhs.Loader.GetAssembly(rhs);
        }
        public static bool operator ==(Assembly lhs, CSharpAssembly rhs) {
            return rhs == lhs;
        }
        public static bool operator !=(CSharpAssembly lhs, Assembly rhs) {
            return !(lhs == rhs);
        }
        public static bool operator !=(Assembly lhs, CSharpAssembly rhs) {
            return !(lhs == rhs);
        }
        public static explicit operator Assembly(CSharpAssembly assembly) {
            return assembly.Load();
        }

        private readonly Loader<object, CSharpType> m_typeLoader;
        private readonly Loader<string, CSharpNamespace> m_namespaceLoader;

        internal CSharpAssembly(
            CSharpLoader loader,
            ICSharpAssembly assemblySymbol)
            : base(
                loader,
                assemblySymbol
            ) {

            m_typeLoader = new Loader<object, CSharpType>(
                keys: o => AssemblySymbol.Types(),
                defaultResolver: o => {
                    var name = o.Key;

                    var fullname = name as string;
                    if (fullname != null)
                        return TryGetType(TypeName.TryParse(fullname));

                    var type = Loader.TryGetType(name);
                    if (type.Assembly != this)
                        return null;

                    return type;

                },
                getLoader: o => o.Assembly.m_typeLoader,
                getKeys: o => new object[] {
                    o.MetadataFullName,
                    o.Object
                },
                normalizeKey: o => o
            );

            var namespaces = new Lazy<HashSet<string>>(() =>
                Types().GroupBy(x => x.TypeSymbol.Namespace).Select(x => (string)x.Key).ToHashSet()
            );

            m_namespaceLoader = new Loader<string, CSharpNamespace>(
                keys: o => namespaces.Value,
                defaultResolver: o => {
                    if (!namespaces.Value.Contains(o.Key))
                        return null;
                    return new CSharpNamespace(this, o.Key);
                },
                getLoader: o => o.Assembly.m_namespaceLoader,
                getKeys: o => new[] { o.Name },
                normalizeKey: o => o as string
            );
        }
        internal CSharpType TryGetType(TypeName fullName) {
            if (fullName == null)
                return null;

            // fully qualified?
            if (fullName.HasAssembly)
                return Loader.TryGetType(fullName);

            // resolve element type
            if (fullName.HasElementType) {
                var elementType = TryGetType(fullName.ElementType);

                if (fullName.IsPointer)
                    return elementType.MakePointerType();

                if (fullName.IsSzArray)
                    return elementType.MakeArrayType();

                if (fullName.IsArray)
                    return elementType.MakeArrayType(fullName.ArrayRank);

                throw new NotImplementedException();
            }

            int arity;

            // try get named namespace (or global namespace) from assembly
            var typeNames = fullName.ToLinkList(o => o.DeclaringType).ToStack();
            var typeName = typeNames.Pop();

            // try get type from namespace
            var name = typeName.GetCSharpName(out arity);
            var type = Loader.TryGetType(AssemblySymbol.GetType(name, typeName.Namespace, arity));
            if (type == null)
                return null;

            // try get nested types
            while ((typeName = typeNames.PopOrDefault()) != null) {

                // resolve symbol
                name = typeName.GetCSharpName(out arity);
                type = type.GetNestedType(name, arity: arity, declaredOnly: true);
                if (type == null)
                    return null;
            }

            // generic arguments
            if (fullName.HasGenericArguments) {

                // load generic arguments
                var genericArguments = fullName.GenericArguments()
                    .Select(o => Loader.TryGetType(Loader.TryGetType(o)))
                    .ToArray();
                if (genericArguments.Any(o => o == null))
                    return null;

                // construct generic type
                type = type.MakeGenericType(genericArguments);
            }

            // return result
            return type;
        }

        internal ICSharpAssembly AssemblySymbol {
            get { return (ICSharpAssembly)Symbol; }
        }

        internal CSharpType TryGetType(TypeSymbol type) {
            return m_typeLoader.TryGet(type);
        }
        internal CSharpType GetType(TypeSymbol type) {
            return m_typeLoader.Get(type);
        }

        public string Location {
            get { return AssemblySymbol.Location; }
        }
        public CSharpAssemblyName AssemblyName {
            get { return AssemblySymbol.AssemblyName; }
        }
        public bool IsMscorlib {
            get { throw new NotImplementedException(); }
        }

        public CSharpType TryGetType(string name) {
            if (name.Contains("["))
                return TryGetType(
                    TypeName.Parse(name));

            return m_typeLoader.TryGet(name);
        }
        public CSharpType GetType(string name) {
            if (name.Contains("["))
                return TryGetType(
                    TypeName.Parse(name));

            return m_typeLoader.Get(name);
        }

        public CSharpType TryGetType(Type type) {
            if (type == null)
                return null;

            return TryGetType(type.AssemblyQualifiedName);
        }
        public CSharpType GetType(Type type) {
            if (type == null)
                throw new ArgumentNullException();

            return GetType(type.AssemblyQualifiedName);
        }

        public IEnumerable<CSharpType> Types() {
            return m_typeLoader.Values();
        }

        public CSharpNamespace GetNamespace(string ns) {
            return m_namespaceLoader.Get(ns);
        }
        public CSharpNamespace TryGetNamespace(string ns) {
            return m_namespaceLoader.TryGet(ns);
        }
        public IEnumerable<CSharpNamespace> Namespaces() {
            return m_namespaceLoader.Values();
        }

        public Assembly Load() {
            return AssemblySymbol.Load();
        }

        public override string ToString() {
            return Location ?? AssemblyName.ToString();
        }
        public override bool Equals(object obj) {
            if (obj is Assembly)
                return this == (Assembly)obj;

            if (obj is CSharpAssembly)
                return this == (CSharpAssembly)obj;

            return false;
        }
    }
    public sealed class CSharpNamespace : CSharpLoaderObject {
        public static implicit operator string(CSharpNamespace ns) {
            if (ns == null)
                return null;

            return ns.Name;
        }

        private readonly CSharpAssembly m_assembly;
        private readonly string m_name;

        internal CSharpNamespace(CSharpAssembly assembly, string name)
            : base(assembly.Loader) {

            m_assembly = assembly;
            m_name = name;
        }

        public string Name {
            get { return m_name; }
        }
        public CSharpAssembly Assembly {
            get { return m_assembly; }
        }

        public CSharpType TryGetType(string name) {
            if (name == null)
                return null;

            var fullname = new TextJoin('.') { Name, name }.ReadToEnd();
            return Assembly.TryGetType(fullname);
        }
        public CSharpType GetType(string name) {
            var result = TryGetType(name);
            if (result == null)
                throw new ArgumentException();
            return result;
        }

        public IEnumerable<CSharpType> Types() {
            return Assembly.Types().Where(o => o.Namespace == this);
        }

        public override string ToString() {
            return Assembly.ToString() + ":" + Name;
        }
    }
    public abstract class CSharpMember : CSharpSymbol {
        private Lazy<CSharpType> m_declaringType;
        private Lazy<CSharpMethod> m_declaringMethod;
        private Lazy<CSharpAssembly> m_declaringAssembly;

        internal CSharpMember(CSharpLoader loader, ICSharpMember member)
            : base(loader, member) {

            m_declaringAssembly = new Lazy<CSharpAssembly>(() =>
                Loader.GetAssembly(member.DeclaringAssembly)
            );

            m_declaringType = new Lazy<CSharpType>(() =>
                Loader.TryGetSymbol(member.DeclaringMember) as CSharpType
            );

            m_declaringMethod = new Lazy<CSharpMethod>(() =>
                Loader.TryGetSymbol(member.DeclaringMember) as CSharpMethod
            );
        }

        internal ICSharpMember Member {
            get { return (ICSharpMember)Symbol; }
        }

        public MemberInfo Load() {
            return ((dynamic)this).Load();
        }

        public CSharpType DeclaringType {
            get { return m_declaringType.Value; }
        }
        public CSharpMethod DeclaringMethod {
            get { return m_declaringMethod.Value; }
        }
        public CSharpAssembly Assembly {
            get { return m_declaringAssembly.Value; }
        }

        public bool IsPublic {
            get { return Member.IsPublic; }
        }
        public bool IsPrivate {
            get { return Member.IsPrivate; }
        }
        public bool IsInternal {
            get { return Member.IsInternal; }
        }
        public bool IsProtected {
            get { return Member.IsProtected; }
        }

        public override string ToString() {
            return Name;
        }
    }
    public sealed class CSharpType : CSharpMember {
        public static bool operator ==(CSharpType lhs, Type rhs) {
            if ((object)lhs == null || (object)rhs == null)
                return (object)rhs == (object)lhs;

            return lhs == lhs.Loader.GetType(rhs);
        }
        public static bool operator ==(Type lhs, CSharpType rhs) {
            return rhs == lhs;
        }
        public static bool operator !=(CSharpType lhs, Type rhs) {
            return !(lhs == rhs);
        }
        public static bool operator !=(Type lhs, CSharpType rhs) {
            return !(lhs == rhs);
        }
        public static explicit operator Type(CSharpType type) {
            return type.Load();
        }

        private Lazy<string> m_nameWithoutArity;

        internal CSharpType(CSharpLoader loader, ICSharpType type)
            : base(loader, type) {

            m_nameWithoutArity = new Lazy<string>(() =>
                Regex.Replace(Name, @"`\d*", "")
            );
        }

        private TypeName GetTypeName(bool strongNamed = false, bool genericArguments = false) {

            TypeName typeName = null;

            if (ElementType != null) {
                // element type; add mangling after elementType's name
                var elementType = ElementType.GetTypeName(strongNamed, genericArguments);

                // pointer type
                if (IsPointer)
                    typeName = elementType.MakePointer();

                // array type
                else if (ArrayRank == 1)
                    typeName = elementType.MakeArray();

                // szArray type
                else
                    typeName = elementType.MakeArray(ArrayRank);

            } else {
                var metadataName = MetadataName;

                if (DeclaringType == null) {
                    // root type; add namespace and assembly
                    var assemblyName = strongNamed ? Assembly.AssemblyName.ToString() : null;
                    typeName = TypeName.Create(metadataName, TypeSymbol.Namespace, assemblyName);

                } else {
                    // nested type; add only nested type name
                    var declaringType = DeclaringType.GetTypeName(strongNamed);
                    typeName = declaringType.MakeNestedType(metadataName);
                }

                // generic type; add generic arguments if asked to provide them
                if (genericArguments && IsGenericType && !ContainsGenericParameters)
                    typeName = typeName.MakeGenericType(GenericArguments().Select(o => o.GetTypeName(true, true)));
            }


            return typeName;
        }
        private string ElementNameDecoration {
            get {
                if (!HasElementType)
                    return null;

                var result = default(string);

                if (IsPointer)
                    return result + "*";

                if (IsArray)
                    return result + "[" + "".PadLeft(ArrayRank - 1, ',') + "]";

                return result;
            }
        }

        private T GetMember<T>(
            string name = null,
            object types = null,
            int arity = 0,
            bool? isPublic = null,
            bool? isStatic = null,
            bool declaredOnly = false,
            bool useExactBinding = false)
            where T : CSharpMember {

            return Members<T>(
                name,
                types,
                arity,
                isPublic,
                isStatic,
                declaredOnly,
                useExactBinding
            ).SingleOrDefault();
        }

        private IEnumerable<T> Members<T>(
            string name = null,
            object types = null,
            int arity = 0,
            bool? isPublic = null,
            bool? isStatic = null,
            bool declaredOnly = false,
            bool useExactBinding = false)
            where T : CSharpMember {

            if (IsGenericParameter)
                return Enumerable.Empty<T>();

            return Members(
                name: name,
                types: types,
                arity: arity,
                isPublic: isPublic,
                isStatic: isStatic,
                declaredOnly: declaredOnly,
                useExactBinding: useExactBinding
            ).OfType<T>();
        }

        internal bool TypeCheckEnum(dynamic value) {

            var type = ((object)value).GetType();
            if (type.IsPrimitive)
                return true;

            if (!type.IsEnum)
                return false;

            return Loader.TryGetType(type) == this;
        }
        internal ICSharpType TypeSymbol {
            get { return (ICSharpType)Symbol; }
        }

        #region Identity
        public CSharpType BaseType {
            get { return Loader.TryGetType(TypeSymbol.BaseType); }
        }
        public IEnumerable<CSharpType> Interfaces() {
            return TypeSymbol.Interfaces().Select(o => Loader.GetType(o));
        }
        public bool Implements(CSharpType type) {
            return Interfaces().Contains(type);
        }
        public bool Implements(Type type) {
            return Implements(Loader.GetType(type));
        }
        public Type RuntimeType {
            get { throw new NotImplementedException(); }
        }
        #endregion

        #region ElementType
        public bool HasElementType {
            get { return ElementType != null; }
        }
        public CSharpType ElementType {
            get { return Loader.TryGetType(TypeSymbol.ElementType); }
        }
        public CSharpType RootElementType {
            get {
                if (!HasElementType)
                    return this;

                var elementType = this;
                while (elementType.HasElementType)
                    elementType = elementType.ElementType;

                return elementType;
            }
        }
        public CSharpType MakePointerType() {
            return Loader.TryGetType(TypeSymbol.MakePointerType());
        }
        public CSharpType MakeArrayType(int? rank = null) {
            return Loader.TryGetType(TypeSymbol.MakeArrayType(rank));
        }
        public int ArrayRank {
            get { return TypeSymbol.ArrayRank; }
        }
        #endregion

        #region Name
        public string MetadataName {
            get { return TypeSymbol.MetadataName; }
        }
        public string MetadataFullName {
            get {
                if (ContainsGenericParameters && !IsGenericTypeDefinition)
                    return null;

                if (IsGenericParameter)
                    return null;

                return GetTypeName(genericArguments: true).ToString();
            }
        }
        public string AssemblyQualifiedName {
            get {
                if (ContainsGenericParameters && !IsGenericTypeDefinition)
                    return null;

                if (IsGenericParameter)
                    return null;

                return GetTypeName(strongNamed: true, genericArguments: true).ToString();
            }
        }
        public string KeywordName {
            get { return Loader.TryGetTypeKeyword(this); }
        }
        public override string Name {
            get {
                if (HasElementType)
                    return ElementType.Name + ElementNameDecoration;

                return Symbol.Name;
            }
        }
        public CSharpNamespace Namespace {
            get { return Assembly.TryGetNamespace(TypeSymbol.Namespace); }
        }
        public string NameWithoutArity {
            get { return m_nameWithoutArity.Value; }
        }
        #endregion

        #region Modifiers
        public bool IsAbstract {
            get { return TypeSymbol.IsAbstract; }
        }
        public bool IsSealed {
            get { return TypeSymbol.IsSealed; }
        }
        public bool IsStatic {
            get { return TypeSymbol.IsStatic; }
        }
        #endregion

        #region Type of Type
        public bool IsValueType {
            get { return TypeSymbol.IsValueType; }
        }
        public bool IsReferenceType {
            get { return TypeSymbol.IsReferenceType; }
        }

        public bool IsInterface {
            get { return TypeSymbol.IsInterface; }
        }
        public bool IsClass {
            get { return TypeSymbol.IsClass; }
        }
        public bool IsStruct {
            get { return TypeSymbol.IsStruct; }
        }
        public bool IsEnum {
            get { return TypeSymbol.IsEnum; }
        }
        public bool IsArray {
            get { return TypeSymbol.IsArray; }
        }
        public bool IsDelegate {
            get { return TypeSymbol.IsDelegate; }
        }
        public bool IsPointer {
            get { return TypeSymbol.IsPointer; }
        }
        #endregion

        #region Generic Type
        public int GenericArity {
            get { return TypeSymbol.GenericArity; }
        }
        public bool IsGenericType {
            get { return GenericArguments().Any(); }
        }
        public bool IsGenericTypeDefinition {
            get { return IsGenericType && this == GenericTypeDefinition; }
        }
        public bool ContainsGenericParameters {
            get {
                if (IsGenericTypeDefinition)
                    return true;

                return GenericArguments().Any(o => o.IsGenericParameter || o.ContainsGenericParameters);
            }
        }
        public CSharpType GenericTypeDefinition {
            get { return Loader.TryGetType(TypeSymbol.GenericTypeDefinition); }
        }
        public IEnumerable<CSharpType> GenericArguments() {
            return TypeSymbol.GenericArguments().Select(o => Loader.GetType(o));
        }
        public CSharpType MakeGenericType(IEnumerable<CSharpType> genericArguments) {
            return MakeGenericType(genericArguments.ToArray());
        }
        public CSharpType MakeGenericType(params CSharpType[] genericArguments) {
            return Loader.GetType(TypeSymbol.MakeGenericType(genericArguments.Select(o => o.Symbol.Object)));
        }

        public bool IsSubclassOf(CSharpType type) {
            return TypeSymbol.IsSubclassOf(type.TypeSymbol);
        }
        public bool IsSubclassOf(Type type) {
            return IsSubclassOf(Loader.GetType(type));
        }
        public bool IsOrIsSubclassOf(CSharpType type) {
            return type == this || TypeSymbol.IsSubclassOf(type.TypeSymbol);
        }
        public bool IsOrIsSubclassOf(Type type) {
            return IsOrIsSubclassOf(Loader.GetType(type));
        }
        public CSharpType GetGenericSubClass(Type genericTypeDefinition) {
            return GetGenericSubClass(Loader.GetType(genericTypeDefinition));
        }
        public CSharpType GetGenericSubClass(CSharpType genericTypeDefinition) {
            return this.ToLinkList(o => o.BaseType)
                .SingleOrDefault(o => o.GenericTypeDefinition == genericTypeDefinition);
        }
        #endregion

        #region Generic Parameter
        public bool IsGenericParameter {
            get { return TypeSymbol.IsGenericParameter; }
        }
        public int GenericParameterPosition {
            get { return TypeSymbol.GenericParameterPosition; }
        }
        public IEnumerable<CSharpType> GenericParameterConstraints() {
            return TypeSymbol.GenericParameterConstraints().Select(o => Loader.GetType(o));
        }
        public bool HasClassConstraint {
            get { return TypeSymbol.HasClassConstraint; }
        }
        public bool HasStructConstraint {
            get { return TypeSymbol.HasStructConstraint; }
        }
        public bool HasNewConstraint {
            get { return TypeSymbol.HasNewConstraint; }
        }
        public bool HasOutConstraint {
            get { return TypeSymbol.HasOutConstraint; }
        }
        public bool HasInConstraint {
            get { return TypeSymbol.HasInConstraint; }
        }
        #endregion

        #region Members
        public CSharpField GetField(
            string name = null,
            bool? isPublic = null,
            bool? isStatic = null,
            bool declaredOnly = false) {

            return GetMember<CSharpField>(
                name,
                isPublic: isPublic,
                isStatic: isStatic,
                declaredOnly: declaredOnly
            );
        }
        public IEnumerable<CSharpField> Fields(
            string name = null,
            bool? isPublic = null,
            bool? isStatic = null,
            bool declaredOnly = false) {

            return Members<CSharpField>(
                name,
                isPublic: isPublic,
                isStatic: isStatic,
                declaredOnly: declaredOnly
            );
        }
        public CSharpProperty GetProperty(
            string name = null,
            bool? isPublic = null,
            bool? isStatic = null,
            bool declaredOnly = false) {

            return GetMember<CSharpProperty>(
                name,
                isPublic: isPublic,
                isStatic: isStatic,
                declaredOnly: declaredOnly
            );
        }
        public IEnumerable<CSharpProperty> Properties(
            string name = null,
            bool? isPublic = null,
            bool? isStatic = null,
            bool declaredOnly = false) {

            return Members<CSharpProperty>(
                name,
                isPublic: isPublic,
                isStatic: isStatic,
                declaredOnly: declaredOnly
            );
        }
        public CSharpType GetNestedType(
            string name = null,
            int arity = 0,
            bool? isPublic = null,
            bool? isStatic = null,
            bool declaredOnly = false) {

            if (arity > 0)
                name += "`" + arity;

            return GetMember<CSharpType>(
                name,
                arity: 0,
                isPublic: isPublic,
                isStatic: isStatic,
                declaredOnly: declaredOnly
            );
        }
        public IEnumerable<CSharpType> NestedTypes(
            string name = null,
            int arity = 0,
            bool? isPublic = null,
            bool? isStatic = null,
            bool declaredOnly = false) {

            return Members<CSharpType>(
                name,
                arity: arity,
                isPublic: isPublic,
                isStatic: isStatic,
                declaredOnly: declaredOnly
            );
        }

        public CSharpMethod GetMethod(
            string name = null,
            object types = null,
            bool? isPublic = null,
            bool? isStatic = null,
            bool declaredOnly = false,
            bool useExactBinding = false) {

            return GetMember<CSharpMethod>(
                name,
                types: types,
                isPublic: isPublic,
                isStatic: isStatic,
                declaredOnly: declaredOnly,
                useExactBinding: useExactBinding
            );
        }

        public IEnumerable<CSharpMethod> Methods(
            string name = null,
            object types = null,
            bool? isPublic = null,
            bool? isStatic = null,
            bool declaredOnly = false,
            bool useExactBinding = false) {

            return Members<CSharpMethod>(
                name,
                types: types,
                isPublic: isPublic,
                isStatic: isStatic,
                declaredOnly: declaredOnly,
                useExactBinding: useExactBinding
            );
        }

        public CSharpMember GetMember(
            string name = null,
            object types = null,
            int arity = 0,
            bool? isPublic = null,
            bool? isStatic = null,
            bool declaredOnly = false,
            bool useExactBinding = false) {

            return Members(
                name,
                types,
                arity,
                isPublic,
                isStatic,
                declaredOnly,
                useExactBinding
            ).Single();
        }

        public IEnumerable<CSharpMember> Members(
            string name = null,
            object types = null,
            int arity = 0,
            bool? isPublic = null,
            bool? isStatic = null,
            bool declaredOnly = false,
            bool useExactBinding = false) {

            return TypeSymbol.Members(
                name: name,
                types: types,
                arity: arity,
                isPublic: isPublic,
                isStatic: isStatic,
                declaredOnly: declaredOnly,
                useExactBinding: useExactBinding
            ).Select(o => (CSharpMember)Loader.GetSymbol(o));
        }
        #endregion

        #region Enum
        private bool? m_enumContainsFlagsCache;

        public bool EnumContainsFlags {
            get {
                if (!IsEnum)
                    return false;

                if (m_enumContainsFlagsCache == null)
                    m_enumContainsFlagsCache = IsDefined(Loader.GetType(typeof(FlagsAttribute)));

                return (bool)m_enumContainsFlagsCache;
            }
        }
        public CSharpEnumField GetEnumField(string name) {
            return (CSharpEnumField)GetMember<CSharpField>(name);
        }
        public IEnumerable<CSharpEnumField> EnumFields() {
            if (!IsEnum)
                return Enumerable.Empty<CSharpEnumField>();

            var fields =
                from o in Fields().Cast<CSharpEnumField>()
                orderby o.IsEnumUberFlag, (long)o.ConstantValue
                select o;

            return fields;
        }
        public IEnumerable<CSharpEnumField> EnumValues(dynamic value = null, bool minimumSet = true) {
            if (value == null)
                minimumSet = false;

            if (!IsEnum)
                return Enumerable.Empty<CSharpEnumField>();

            var result = EnumFields().Where(o => !o.IsEnumMask);

            if (value != null)
                result = result.Where(o => o.TestEnumValue(value));

            if (minimumSet) {
                var set = result.ToArray();
                result = result.Where(o => set.All(x => o == x || !o.TestEnumValue(x)));
            }

            return result;
        }
        public IEnumerable<CSharpEnumField> EnumMasks() {
            if (!IsEnum)
                return Enumerable.Empty<CSharpEnumField>();

            return EnumFields().Where(o => o.IsEnumMask);
        }

        public CSharpEnumField GetEnumValue(dynamic value) {
            if (!TypeCheckEnum(value))
                return null;

            var lValue = (long)value;
            return EnumValues().SingleOrDefault(o => (long)o.ConstantValue == lValue);
        }
        public CSharpEnumField GetEnumMask(dynamic value) {
            if (!TypeCheckEnum(value))
                return null;

            var lValue = (long)value;
            return EnumMasks().SingleOrDefault(o => (long)o.ConstantValue == lValue);
        }
        #endregion

        public new Type Load() {
            return Type.GetType(AssemblyQualifiedName, throwOnError: true);
        }
        public CSharpTypedConstant DefaultValue {
            get { return new CSharpTypedConstant(Loader, TypeSymbol.DefaultValue); }
        }

        public override string ToString() {
            return MetadataFullName;
        }
        public override bool Equals(object obj) {
            if (obj is Type)
                return this == (Type)obj;

            if (obj is CSharpType)
                return this == (CSharpType)obj;

            return false;
        }
    }
    public sealed class CSharpMethod : CSharpMember {

        internal CSharpMethod(CSharpLoader loader, ICSharpMethod methodSymbol)
            : base(loader, methodSymbol) {
        }
        private ICSharpMethod MethodSymbol {
            get { return (ICSharpMethod)Symbol; }
        }

        #region Kind
        public bool IsConstructor {
            get { return MethodSymbol.IsConstructor; }
        }
        public bool IsFinalizer {
            get { return MethodSymbol.IsFinalizer; }
        }

        public bool IsPropertyGet {
            get { return MethodSymbol.IsPropertyGet; }
        }
        public bool IsPropertySet {
            get { return MethodSymbol.IsPropertySet; }
        }
        public bool IsEventAdd {
            get { return MethodSymbol.IsEventAdd; }
        }
        public bool IsEventRemove {
            get { return MethodSymbol.IsEventRemove; }
        }

        public bool IsAnonymousFunction {
            get { return MethodSymbol.IsAnonymousFunction; }
        }
        public bool IsExplicitInterfaceImplementation {
            get { return MethodSymbol.IsExplicitInterfaceImplementation; }
        }
        public bool IsDelegateInvoke {
            get { return MethodSymbol.IsDelegateInvoke; }
        }

        public bool IsOperator {
            get { return MethodSymbol.IsOperator; }
        }
        public bool IsConversion {
            get { return MethodSymbol.IsConversion; }
        }
        #endregion

        #region Modifiers
        public bool IsVirtual {
            get { return MethodSymbol.IsVirtual; }
        }
        public bool IsAbstract {
            get { return MethodSymbol.IsAbstract; }
        }
        public bool IsOverride {
            get { return MethodSymbol.IsOverride; }
        }
        public bool IsSealed {
            get { return MethodSymbol.IsSealed; }
        }
        public bool IsExtern {
            get { return MethodSymbol.IsExtern; }
        }
        public bool IsStatic {
            get { return MethodSymbol.IsStatic; }
        }
        #endregion

        public IEnumerable<CSharpParameter> Parameters() {
            return MethodSymbol.Parameters().Select(o => (CSharpParameter)Loader.GetSymbol(o));
        }
        public CSharpType ReturnType {
            get { return Loader.TryGetType(MethodSymbol.ReturnType); }
        }

        #region Generic
        public bool IsGenericMethod {
            get { return MethodSymbol.IsGenericMethod; }
        }
        public bool IsGenericMethodDefinition {
            get { return MethodSymbol.IsGenericMethodDefinition; }
        }
        public bool HasGenericArguments {
            get { return GenericArguments().Any(); }
        }
        public CSharpMethod GenericMethodDefinition {
            get { return (CSharpMethod)Loader.GetSymbol(MethodSymbol.GenericMethodDefinition); }
        }
        public IEnumerable<CSharpType> GenericArguments() {
            return MethodSymbol.GenericArguments().Select(o => Loader.GetType(o));
        }
        public CSharpMethod MakeGenericMethod(IEnumerable<CSharpType> genericArguments) {
            return (CSharpMethod)Loader.GetSymbol(
                MethodSymbol.MakeGenericMethod(
                    genericArguments.Select(o => o.Symbol)
                )
            );
        }
        public CSharpMethod MakeGenericMethod(params CSharpType[] genericArguments) {
            return (CSharpMethod)MakeGenericMethod((IEnumerable<CSharpType>)genericArguments);
        }
        #endregion

        public new MethodBase Load() {
            throw new NotImplementedException();
        }
    }
    public sealed class CSharpParameter : CSharpSymbol {

        internal CSharpParameter(CSharpLoader loader, ICSharpParameter parameterSymbol)
            : base(loader, parameterSymbol) {
        }

        private ICSharpParameter ParameterSymbol {
            get { return (ICSharpParameter)Symbol; }
        }

        public int Position {
            get { return ParameterSymbol.Position; }
        }
        public CSharpType Type {
            get { return Loader.GetType(ParameterSymbol.Type); }
        }

        public CSharpType DeclaringType {
            get { return Loader.TryGetType(ParameterSymbol.DeclaringType); }
        }
        public CSharpMethod DeclaringMethod {
            get { return (CSharpMethod)Loader.TryGetSymbol(ParameterSymbol.DeclaringMethod); }
        }
        public CSharpProperty DeclaringProperty {
            get { return (CSharpProperty)Loader.TryGetSymbol(ParameterSymbol.DeclaringProperty); }
        }

        public dynamic DefaultValue {
            get {
                var result = ParameterSymbol.DefaultValue;
                if (result == null)
                    return null;

                if (Type.IsEnum)
                    result = new CSharpSyntheticEnum(Type, result);

                return result;
            }
        }
        public bool HasDefaultValue {
            get { return ParameterSymbol.HasDefaultValue; }
        }

        public bool IsExtern {
            get { return ParameterSymbol.IsExtern; }
        }
        public bool IsOptional {
            get { return ParameterSymbol.IsOptional; }
        }
        public bool IsParams {
            get { return ParameterSymbol.IsParams; }
        }
        public bool IsThis {
            get { return ParameterSymbol.IsThis; }
        }
        public bool IsOut {
            get { return ParameterSymbol.IsOut; }
        }
        public bool IsRef {
            get { return ParameterSymbol.IsRef; }
        }

        public new ParameterInfo Load() {
            throw new NotImplementedException();
        }

        public override string ToString() {
            return ParameterSymbol.Name;
        }
    }
    public abstract class CSharpDataMember : CSharpMember {

        internal CSharpDataMember(CSharpLoader loader, ICSharpDataMember dataMemberSymbol)
            : base(loader, dataMemberSymbol) {
        }

        public abstract CSharpType MemberType { get; }
    }
    public class CSharpField : CSharpDataMember {

        private readonly Lazy<FieldInfo> m_fieldInfo;

        internal CSharpField(CSharpLoader loader, ICSharpField fieldSymbol)
            : base(loader, fieldSymbol) {

            m_fieldInfo = new Lazy<FieldInfo>(() => {
                var declaringType = DeclaringType.Load();
                var result = declaringType.Get<FieldInfo>(
                    Name,
                    isInstance: true,
                    isStatic: true,
                    declaredOnly: true
                ).Single();

                return result;
            });
        }

        internal ICSharpField FieldSymbol {
            get { return (ICSharpField)Symbol; }
        }

        public override CSharpType MemberType {
            get { return Loader.GetType(FieldSymbol.Type); }
        }

        public object LoadValue() {
            var result = SyntheticConstantValue;
            if (result is CSharpSyntheticEnum)
                return (Enum)result;
            return result;
        }
        public dynamic ConstantValue {
            get { return FieldSymbol.ConstantValue.Value; }
        }
        public dynamic SyntheticConstantValue {
            get {
                var result = ConstantValue;
                if (result == null)
                    return result;

                if (MemberType.IsEnum)
                    result = new CSharpSyntheticEnum(MemberType, result);

                return result;
            }
        }
        public bool HasConstantValue {
            get { return FieldSymbol.HasConstantValue; }
        }
        public bool IsConst {
            get { return FieldSymbol.IsConst; }
        }
        public bool IsReadOnly {
            get { return FieldSymbol.IsReadOnly; }
        }

        public new FieldInfo Load() {
            return m_fieldInfo.Value;
        }
    }
    public sealed class CSharpEnumField : CSharpField {
        private CSharpField m_enumMask;
        private bool m_isEnumMaskCached;

        internal CSharpEnumField(CSharpLoader loader, ICSharpField fieldSymbol)
            : base(loader, fieldSymbol) {
        }

        public bool IsEnumMask {
            get {
                if (!MemberType.EnumContainsFlags)
                    return false;

                return Name.EndsWith("Mask");
            }
        }
        public bool IsEnumFlag {
            get { return MemberType.EnumContainsFlags && !IsEnumMask && !HasEnumMask && !IsEnumDefaultValue; }
        }
        public bool IsEnumUberFlag {
            get { return MemberType.EnumContainsFlags && ConstantValue == -1; }
        }
        public bool IsEnumDefaultValue {
            get { return MemberType.IsEnum && ConstantValue == 0; }
        }

        public bool HasEnumMask {
            get { return EnumMask != null; }
        }
        public CSharpField EnumMask {
            get {
                if (!MemberType.EnumContainsFlags || IsEnumMask || IsEnumDefaultValue)
                    return null;

                if (!m_isEnumMaskCached) {
                    m_enumMask = MemberType.EnumMasks().SingleOrDefault(mask =>
                        ((long)ConstantValue & (long)mask.ConstantValue) == (long)ConstantValue);
                    m_isEnumMaskCached = true;
                }

                return m_enumMask;
            }
        }
        public IEnumerable<CSharpField> EnumMaskValues() {
            if (!IsEnumMask)
                return Enumerable.Empty<CSharpField>();

            return MemberType.EnumValues().Where(value => !value.IsEnumDefaultValue &&
                ((long)ConstantValue & (long)value.ConstantValue) == (long)value.ConstantValue);
        }
        public IEnumerable<CSharpField> AggregatingEnumFlags() {
            if (!IsEnumFlag)
                return Enumerable.Empty<CSharpField>();

            return MemberType.EnumValues().Where(o => o.AggregatedEnumFlags().Any(x => x == this));
        }
        public IEnumerable<CSharpField> AggregatedEnumFlags() {
            if (!IsEnumFlag)
                return Enumerable.Empty<CSharpField>();

            return MemberType.EnumValues().Except(new[] { this })
                .Where(o => !o.IsEnumDefaultValue && o.IsEnumFlag)
                .Where(o => ((long)ConstantValue & (long)o.ConstantValue) == (long)o.ConstantValue);
        }

        public bool TestEnumValue(dynamic value) {

            // allow CSharpField to be passed in as a value
            var asField = value as CSharpField;
            if (asField != null) {
                if (asField.MemberType != MemberType)
                    return false;
                value = asField.ConstantValue;
            }

            // ensure the field is an enum value
            if (!MemberType.IsEnum)
                throw new InvalidOperationException();

            if (value == null)
                throw new ArgumentException();

            // type check the argument
            if (!MemberType.TypeCheckEnum(value))
                return false;

            // convert the constant value and the argument to longs
            var lConstant = (long)ConstantValue;
            var lValue = (long)value;

            // test if both are zero
            if (lValue == 0 || lConstant == 0)
                return lValue == lConstant;

            // mask the value
            if (HasEnumMask || IsEnumMask) {
                var mask = EnumMask ?? this;
                lValue &= (long)mask.ConstantValue;
            }

            // do any masked enum values match the masked value
            if (IsEnumMask)
                return EnumMaskValues().Any(o => (long)o.ConstantValue == lValue);

            // this can be flag without a mask if the enum has FlagsAttribute set
            else if (IsEnumFlag)
                return (lValue & lConstant) == lConstant;

            // check the value
            return lValue == lConstant;
        }

        public new FieldInfo Load() {
            throw new NotImplementedException();
        }
    }
    public sealed class CSharpProperty : CSharpDataMember {

        private readonly Lazy<PropertyInfo> m_propertyInfo;

        internal CSharpProperty(CSharpLoader loader, ICSharpProperty propertySymbol)
            : base(loader, propertySymbol) {

            m_propertyInfo = new Lazy<PropertyInfo>(() => {
                var declaringType = DeclaringType.Load();
                var result = declaringType.Get<PropertyInfo>(
                    Name,
                    isInstance: true,
                    isStatic: true,
                    declaredOnly: true
                ).Single();

                return result;
            });
        }

        private ICSharpProperty PropertySymbol {
            get { return (ICSharpProperty)Symbol; }
        }

        public override CSharpType MemberType {
            get { return Loader.GetType(PropertySymbol.Type); }
        }

        public bool IsAbstract {
            get { return PropertySymbol.IsAbstract; }
        }
        public bool IsExtern {
            get { return PropertySymbol.IsExtern; }
        }
        public bool IsOverride {
            get { return PropertySymbol.IsOverride; }
        }
        public bool IsSealed {
            get { return PropertySymbol.IsSealed; }
        }
        public bool IsStatic {
            get { return PropertySymbol.IsStatic; }
        }
        public bool IsVirtual {
            get { return PropertySymbol.IsVirtual; }
        }

        public CSharpProperty OverriddenProperty {
            get { return (CSharpProperty)Loader.TryGetSymbol(PropertySymbol.OverriddenProperty); }
        }

        public bool IsReadOnly {
            get { return PropertySymbol.IsReadOnly; }
        }
        public bool IsWriteOnly {
            get { return PropertySymbol.IsWriteOnly; }
        }

        public CSharpMethod GetMethod {
            get { return (CSharpMethod)Loader.TryGetSymbol(PropertySymbol.GetMethod); }
        }
        public CSharpMethod SetMethod {
            get { return (CSharpMethod)Loader.TryGetSymbol(PropertySymbol.SetMethod); }
        }

        public bool IsIndexer {
            get { return PropertySymbol.IsIndexer; }
        }
        public IEnumerable<CSharpParameter> Parameters() {
            return PropertySymbol.Parameters().Select(o => (CSharpParameter)Loader.GetSymbol(o));
        }

        public new PropertyInfo Load() {
            return m_propertyInfo.Value;
        }
    }
    public sealed class CSharpEvent : CSharpMember {

        internal CSharpEvent(CSharpLoader loader, ICSharpEvent eventSymbol)
            : base(loader, eventSymbol) {
        }

        private EventSymbol EventSymbol {
            get { return (EventSymbol)Symbol; }
        }

        public new EventInfo Load() {
            throw new NotImplementedException();
        }
    }

    public sealed class CSharpAttribute : CSharpLoaderObject {

        private ICSharpAttribute m_attribute;
        private CSharpSyntheticAttribute m_value;

        internal CSharpAttribute(CSharpLoader loader, ICSharpAttribute attribute)
            : base(loader) {
            m_attribute = attribute;
        }

        public CSharpType Type {
            get { return Loader.GetType(m_attribute.Type); }
        }
        public CSharpMethod Constructor {
            get { return (CSharpMethod)Loader.GetSymbol(m_attribute.Constructor); }
        }
        public IEnumerable<KeyValuePair<CSharpMember, CSharpTypedConstant>> NamedArguments() {
            return m_attribute.NamedArguments().Select(o =>
                new KeyValuePair<CSharpMember, CSharpTypedConstant>(
                    (CSharpMember)Loader.GetSymbol(o.Key),
                    new CSharpTypedConstant(Loader, o.Value)
                )
            );
        }
        public IEnumerable<CSharpTypedConstant> Arguments() {
            return m_attribute.Arguments().Select(o => new CSharpTypedConstant(Loader, o));
        }
        public dynamic Value {
            get {
                if (m_value == null)
                    m_value = new CSharpSyntheticAttribute(this);
                return m_value;
            }
        }
    }
    public sealed class CSharpTypedConstant : CSharpLoaderObject {

        private ICSharpTypedConstant m_value;

        internal CSharpTypedConstant(CSharpLoader loader, ICSharpTypedConstant value)
            : base(loader) {
            m_value = value;
        }

        public bool IsString {
            get { return m_value.IsString; }
        }
        public bool IsCharacter {
            get { return m_value.IsCharacter; }
        }
        public bool IsPrimitive {
            get { return m_value.IsPrimitive; }
        }
        public bool IsEnum {
            get { return m_value.IsEnum; }
        }
        public bool IsType {
            get { return m_value.IsType; }
        }
        public bool IsArray {
            get { return m_value.IsArray; }
        }

        public CSharpType Type {
            get { return Loader.GetType(m_value.Type); }
        }
        public object Value {
            get {
                if (IsArray)
                    return Values;

                var value = m_value.Value;

                if (IsType)
                    return Loader.TryGetType(value);

                return value;
            }
        }
        public IEnumerable<CSharpTypedConstant> Values {
            get {
                if (!IsArray)
                    throw new InvalidOperationException();

                var compilation = Loader;

                return m_value.Values().Select(o => new CSharpTypedConstant(compilation, o));
            }
        }
        public int Length {
            get {
                if (!IsArray)
                    throw new InvalidOperationException();
                return m_value.Values().Count();
            }
        }

        public object Load() {
            if (IsEnum)
                return Enum.ToObject(Type.Load(), Value);

            if (IsArray) {
                var values = Values.Select(o => o.Load()).ToArray();
                var arrayType = Type.Load();
                var elementType = arrayType.GetElementType();
                var array = Array.CreateInstance(elementType, values.Length);
                Array.Copy(values, array, values.Length);
                return array;
            }

            if (IsType)
                return ((CSharpType)Value).Load();

            return Value;
        }
    }

    public interface ICSharpEnum {
        IEnumerable<CSharpEnumField> Values();
        CSharpType GetCSharpType();
    }
    public sealed class CSharpSyntheticAttribute : DynamicObject {
        private static IEnumerable<KeyValuePair<string, CSharpTypedConstant>> DynamicProperties(CSharpAttribute data) {

            var namedArguments =
                from a in data.NamedArguments()
                select new {
                    Name = a.Key.Name,
                    Value = a.Value
                };

            var arguments =
                data.Constructor.Parameters()
                .Zip(data.Arguments(), (p, a) => new {
                    Name = p.Name.ToUpperFirst(),
                    Value = a
                });

            var properties =
                from member in data.Type.Members(isStatic: false, isPublic: true)
                where member is CSharpDataMember
                let dataMember = (CSharpDataMember)member
                join argument in namedArguments.Union(arguments)
                    on member.Name equals argument.Name into nameValues

                from nameValue in nameValues.DefaultIfEmpty()
                select new KeyValuePair<string, CSharpTypedConstant>(
                    member.Name,
                    nameValue != null ? nameValue.Value : dataMember.MemberType.DefaultValue
                );

            return properties.ToArray();
        }
        private static dynamic GetDynamicPropertyValue(CSharpTypedConstant constant) {
            if (constant == null)
                return null;

            var loader = constant.Loader;

            // synthisize array and enum dynamic objects
            if (constant.IsArray) {

                // array type is object...
                var arrayType = typeof(object);

                // ...unless all elements are mscorlib primitives
                var elementType = constant.Type.ElementType;
                if (!elementType.IsEnum && elementType != loader.TryGetType(typeof(object)))
                    arrayType = elementType.Load();

                // Type => CSharpType 
                if (arrayType.IsSubclassOfOrIs(typeof(Type)))
                    arrayType = typeof(CSharpType);

                // allocate the array and copy the values into it
                return constant.Values.Select(o => GetDynamicPropertyValue(o)).ToArray(arrayType);
            }

            // synthisized enum
            if (constant.IsEnum)
                return new CSharpSyntheticEnum(constant.Type, constant.Value);

            // mscorlib primitve
            return constant.Value;
        }

        private CSharpAttribute m_data;
        private Lazy<Dictionary<string, object>> m_values;

        internal CSharpSyntheticAttribute(CSharpAttribute data) {
            m_data = data;
            m_values = new Lazy<Dictionary<string, object>>(() => {
                var values = new Dictionary<string, object>();

                foreach (var property in DynamicProperties(m_data)) {
                    var name = property.Key;
                    var constant = property.Value;

                    values[name] = GetDynamicPropertyValue(constant);
                }

                return values;
            });
        }

        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) {
            result = null;

            var name = binder.Name;
            if (name == "GetAttributeType" && args.None()) {
                result = m_data.Type;
                return true;
            }

            if (name == "Load" && args.None()) {

                // activate attribute
                var arguments = m_data.Arguments().Select(o => o.Load()).ToArray();
                result = m_data.Type.Load().Activate(arguments);

                // set properties and fields
                foreach (var namedArgument in m_data.NamedArguments()) {
                    var memberInfo = (MemberInfo)namedArgument.Key.Load();
                    memberInfo.SetValue(result, namedArgument.Value.Load());
                }

                return true;
            }

            return false;
        }
        public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) {
            var success = m_values.Value.TryGetValue(indexes[0] as string, out result);
            return success;
        }
        public override bool TryGetMember(GetMemberBinder binder, out object result) {
            var success = m_values.Value.TryGetValue(binder.Name, out result);
            return success;
        }
    }
    public sealed class CSharpSyntheticEnum : DynamicObject, ICSharpEnum {
        private readonly long m_value;
        private readonly CSharpType m_type;

        internal CSharpSyntheticEnum(CSharpType type, object value) {
            m_value = (long)(dynamic)value;
            m_type = type;
        }

        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) {
            result = null;

            var name = binder.Name;
            if (name == "Load" && args.None()) {
                var type = m_type.Load();
                result = Enum.ToObject(type, m_value);
                return true;
            }

            return false;
        }
        public override bool TryGetMember(GetMemberBinder binder, out object result) {
            var name = binder.Name;
            var enumValue = m_type.GetEnumValue(name);
            if (enumValue == null)
                throw new MissingFieldException();

            result = enumValue.TestEnumValue(m_value);
            return true;
        }
        public override bool TryConvert(ConvertBinder binder, out object result) {
            if (binder.Type.IsEnum || binder.Type == typeof(Enum)) {
                var type = m_type.Load();
                result = Enum.ToObject(type, m_value);
                return true;
            }

            return base.TryConvert(binder, out result);
        }

        public IEnumerable<CSharpEnumField> Values() {
            return m_type.EnumValues(m_value);
        }
        public CSharpType GetCSharpType() {
            return m_type;
        }

        public override string ToString() {
            var values = m_type.EnumValues(m_value);
            if (values.OnlyOne())
                return values.Single().Name;

            return base.ToString();
        }
    }
}
